@ruiapp/rapid-core 0.5.1 → 0.5.3
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/core/server.d.ts +3 -3
- package/dist/dataAccess/entityManager.d.ts +2 -0
- package/dist/helpers/metaHelper.d.ts +2 -2
- package/dist/index.js +215 -88
- package/dist/server.d.ts +3 -3
- package/dist/types.d.ts +5 -0
- package/package.json +1 -1
- package/src/bootstrapApplicationConfig.ts +7 -0
- package/src/core/response.ts +6 -3
- package/src/core/server.ts +3 -2
- package/src/dataAccess/entityManager.ts +214 -81
- package/src/helpers/metaHelper.ts +22 -7
- package/src/plugins/dataManage/actionHandlers/findCollectionEntityById.ts +10 -1
- package/src/server.ts +19 -6
- package/src/types.ts +7 -0
package/dist/core/server.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CreateEntityOptions, EmitServerEventOptions, EntityWatcherType, GetDataAccessorOptions, GetModelOptions, IDatabaseAccessor, IDatabaseConfig, IQueryBuilder, IRpdDataAccessor, RapidServerConfig, RpdApplicationConfig, RpdDataModel, RpdDataModelProperty, RpdServerEventTypes, UpdateEntityByIdOptions } from "../types";
|
|
1
|
+
import { CreateEntityOptions, EmitServerEventOptions, EntityWatcherType, GetDataAccessorOptions, GetModelOptions, IDatabaseAccessor, IDatabaseClient, IDatabaseConfig, IQueryBuilder, IRpdDataAccessor, RapidServerConfig, RpdApplicationConfig, RpdDataModel, RpdDataModelProperty, RpdServerEventTypes, UpdateEntityByIdOptions } from "../types";
|
|
2
2
|
import { IPluginActionHandler, ActionHandler, ActionHandlerContext } from "./actionHandler";
|
|
3
3
|
import { Next, RouteContext } from "./routeContext";
|
|
4
4
|
import EntityManager from "../dataAccess/entityManager";
|
|
@@ -12,8 +12,8 @@ export interface IRpdServer {
|
|
|
12
12
|
registerFacilityFactory(factory: FacilityFactory): any;
|
|
13
13
|
getFacility<TFacility = any>(name: string, options?: any): Promise<TFacility>;
|
|
14
14
|
getDatabaseAccessor(): IDatabaseAccessor;
|
|
15
|
-
queryDatabaseObject: (sql: string, params?: unknown[] | Record<string, unknown
|
|
16
|
-
tryQueryDatabaseObject: (sql: string, params?: unknown[] | Record<string, unknown
|
|
15
|
+
queryDatabaseObject: (sql: string, params?: unknown[] | Record<string, unknown>, client?: IDatabaseClient) => Promise<any[]>;
|
|
16
|
+
tryQueryDatabaseObject: (sql: string, params?: unknown[] | Record<string, unknown>, client?: IDatabaseClient) => Promise<any[]>;
|
|
17
17
|
registerMiddleware(middleware: any): void;
|
|
18
18
|
registerActionHandler(plugin: RapidPlugin, options: IPluginActionHandler): void;
|
|
19
19
|
getActionHandlerByCode(code: string): ActionHandler | undefined;
|
|
@@ -3,6 +3,7 @@ import { IRpdServer, RapidPlugin } from "../core/server";
|
|
|
3
3
|
import { RouteContext } from "../core/routeContext";
|
|
4
4
|
export type FindOneRelationEntitiesOptions = {
|
|
5
5
|
server: IRpdServer;
|
|
6
|
+
routeContext?: RouteContext;
|
|
6
7
|
mainModel: RpdDataModel;
|
|
7
8
|
relationProperty: RpdDataModelProperty;
|
|
8
9
|
relationEntityIds: any[];
|
|
@@ -10,6 +11,7 @@ export type FindOneRelationEntitiesOptions = {
|
|
|
10
11
|
};
|
|
11
12
|
export type FindManyRelationEntitiesOptions = {
|
|
12
13
|
server: IRpdServer;
|
|
14
|
+
routeContext?: RouteContext;
|
|
13
15
|
mainModel: RpdDataModel;
|
|
14
16
|
relationProperty: RpdDataModelProperty;
|
|
15
17
|
mainEntityIds: any[];
|
|
@@ -3,8 +3,8 @@ import { RpdDataModel, RpdDataModelProperty } from "../types";
|
|
|
3
3
|
export declare function isRelationProperty(property: RpdDataModelProperty): boolean;
|
|
4
4
|
export declare function isOneRelationProperty(property: RpdDataModelProperty): boolean;
|
|
5
5
|
export declare function isManyRelationProperty(property: RpdDataModelProperty): boolean;
|
|
6
|
-
export declare function getEntityProperties(server: IRpdServer, model: RpdDataModel): RpdDataModelProperty[];
|
|
7
|
-
export declare function getEntityPropertiesIncludingBase(server: IRpdServer, model: RpdDataModel): RpdDataModelProperty[];
|
|
6
|
+
export declare function getEntityProperties(server: IRpdServer, model: RpdDataModel, predicate?: (item: RpdDataModelProperty) => boolean): RpdDataModelProperty[];
|
|
7
|
+
export declare function getEntityPropertiesIncludingBase(server: IRpdServer, model: RpdDataModel, predicate?: (item: RpdDataModelProperty) => boolean): RpdDataModelProperty[];
|
|
8
8
|
export declare function getEntityPropertyByCode(server: IRpdServer, model: RpdDataModel, propertyCode: string): RpdDataModelProperty | undefined;
|
|
9
9
|
export declare function getEntityProperty(server: IRpdServer, model: RpdDataModel, predicate: (item: RpdDataModelProperty) => boolean): RpdDataModelProperty | undefined;
|
|
10
10
|
export declare function getEntityOwnPropertyByCode(model: RpdDataModel, propertyCode: string): RpdDataModelProperty | undefined;
|
package/dist/index.js
CHANGED
|
@@ -999,7 +999,6 @@ function mergeHeaders(target, source) {
|
|
|
999
999
|
else if (lodash.isObject(source)) {
|
|
1000
1000
|
Object.entries(source).forEach(([key, value]) => target.set(key, value));
|
|
1001
1001
|
}
|
|
1002
|
-
return target;
|
|
1003
1002
|
}
|
|
1004
1003
|
function newResponse(options) {
|
|
1005
1004
|
return new Response(options.body, {
|
|
@@ -1008,6 +1007,7 @@ function newResponse(options) {
|
|
|
1008
1007
|
});
|
|
1009
1008
|
}
|
|
1010
1009
|
class RapidResponse {
|
|
1010
|
+
// TODO: remove this field.
|
|
1011
1011
|
#response;
|
|
1012
1012
|
status;
|
|
1013
1013
|
body;
|
|
@@ -1027,13 +1027,16 @@ class RapidResponse {
|
|
|
1027
1027
|
if (headers) {
|
|
1028
1028
|
mergeHeaders(responseHeaders, headers);
|
|
1029
1029
|
}
|
|
1030
|
-
this
|
|
1030
|
+
this.status = status || 200;
|
|
1031
|
+
this.body = body;
|
|
1032
|
+
this.#response = newResponse({ body, status: this.status, headers: responseHeaders });
|
|
1031
1033
|
}
|
|
1032
1034
|
redirect(location, status) {
|
|
1033
1035
|
this.headers.set("Location", location);
|
|
1036
|
+
this.status = status || 302;
|
|
1034
1037
|
this.#response = newResponse({
|
|
1035
1038
|
headers: this.headers,
|
|
1036
|
-
status: status
|
|
1039
|
+
status: this.status,
|
|
1037
1040
|
});
|
|
1038
1041
|
}
|
|
1039
1042
|
getResponse() {
|
|
@@ -1709,6 +1712,13 @@ var bootstrapApplicationConfig = {
|
|
|
1709
1712
|
type: "text",
|
|
1710
1713
|
required: false,
|
|
1711
1714
|
},
|
|
1715
|
+
{
|
|
1716
|
+
name: "entityDeletingReaction",
|
|
1717
|
+
code: "entityDeletingReaction",
|
|
1718
|
+
columnName: "entity_deleting_reaction",
|
|
1719
|
+
type: "text",
|
|
1720
|
+
required: false,
|
|
1721
|
+
},
|
|
1712
1722
|
{
|
|
1713
1723
|
name: "readonly",
|
|
1714
1724
|
code: "readonly",
|
|
@@ -2116,22 +2126,41 @@ function isOneRelationProperty(property) {
|
|
|
2116
2126
|
function isManyRelationProperty(property) {
|
|
2117
2127
|
return isRelationProperty(property) && property.relation === "many";
|
|
2118
2128
|
}
|
|
2119
|
-
function
|
|
2120
|
-
if (!
|
|
2129
|
+
function getEntityProperties(server, model, predicate) {
|
|
2130
|
+
if (!predicate) {
|
|
2121
2131
|
return model.properties;
|
|
2122
2132
|
}
|
|
2133
|
+
return lodash.filter(model.properties, predicate);
|
|
2134
|
+
}
|
|
2135
|
+
function getEntityPropertiesIncludingBase(server, model, predicate) {
|
|
2136
|
+
if (!model.base) {
|
|
2137
|
+
return getEntityProperties(server, model, predicate);
|
|
2138
|
+
}
|
|
2123
2139
|
const baseModel = server.getModel({
|
|
2124
2140
|
singularCode: model.base,
|
|
2125
2141
|
});
|
|
2126
2142
|
let baseProperties = [];
|
|
2127
2143
|
if (baseModel) {
|
|
2128
|
-
|
|
2144
|
+
if (predicate) {
|
|
2145
|
+
baseProperties = lodash.filter(baseModel.properties, predicate);
|
|
2146
|
+
}
|
|
2147
|
+
else {
|
|
2148
|
+
baseProperties = baseModel.properties;
|
|
2149
|
+
}
|
|
2150
|
+
baseProperties = baseProperties.map((property) => {
|
|
2129
2151
|
property = lodash.cloneDeep(property);
|
|
2130
2152
|
property.isBaseProperty = true;
|
|
2131
2153
|
return property;
|
|
2132
2154
|
});
|
|
2133
2155
|
}
|
|
2134
|
-
|
|
2156
|
+
let properties;
|
|
2157
|
+
if (predicate) {
|
|
2158
|
+
properties = lodash.filter(model.properties, predicate);
|
|
2159
|
+
}
|
|
2160
|
+
else {
|
|
2161
|
+
properties = model.properties;
|
|
2162
|
+
}
|
|
2163
|
+
return [...baseProperties, ...properties];
|
|
2135
2164
|
}
|
|
2136
2165
|
function getEntityPropertyByCode(server, model, propertyCode) {
|
|
2137
2166
|
return getEntityProperty(server, model, (e) => e.code === propertyCode);
|
|
@@ -2536,6 +2565,7 @@ async function findEntities(server, dataAccessor, options) {
|
|
|
2536
2565
|
if (isManyRelation) {
|
|
2537
2566
|
const relationLinks = await findManyRelationLinksViaLinkTable({
|
|
2538
2567
|
server,
|
|
2568
|
+
routeContext,
|
|
2539
2569
|
mainModel: relationModel,
|
|
2540
2570
|
relationProperty,
|
|
2541
2571
|
mainEntityIds: entityIds,
|
|
@@ -2553,6 +2583,7 @@ async function findEntities(server, dataAccessor, options) {
|
|
|
2553
2583
|
if (isManyRelation) {
|
|
2554
2584
|
relatedEntities = await findManyRelatedEntitiesViaIdPropertyCode({
|
|
2555
2585
|
server,
|
|
2586
|
+
routeContext,
|
|
2556
2587
|
mainModel: model,
|
|
2557
2588
|
relationProperty,
|
|
2558
2589
|
mainEntityIds: entityIds,
|
|
@@ -2563,6 +2594,7 @@ async function findEntities(server, dataAccessor, options) {
|
|
|
2563
2594
|
const targetEntityIds = lodash.uniq(lodash.reject(lodash.map(rows, (entity) => entity[relationProperty.targetIdColumnName]), isNullOrUndefined));
|
|
2564
2595
|
relatedEntities = await findOneRelatedEntitiesViaIdPropertyCode({
|
|
2565
2596
|
server,
|
|
2597
|
+
routeContext,
|
|
2566
2598
|
mainModel: model,
|
|
2567
2599
|
relationProperty,
|
|
2568
2600
|
relationEntityIds: targetEntityIds,
|
|
@@ -2779,7 +2811,7 @@ async function convertEntityFiltersToRowFilters(routeContext, server, model, bas
|
|
|
2779
2811
|
tableName: relationProperty.linkTableName,
|
|
2780
2812
|
})} WHERE ${server.queryBuilder.quoteObject(relationProperty.targetIdColumnName)} = ANY($1::int[])`;
|
|
2781
2813
|
const params = [targetEntityIds];
|
|
2782
|
-
const links = await server.queryDatabaseObject(command, params);
|
|
2814
|
+
const links = await server.queryDatabaseObject(command, params, routeContext?.getDbTransactionClient());
|
|
2783
2815
|
const selfEntityIds = links.map((link) => link[relationProperty.selfIdColumnName]);
|
|
2784
2816
|
replacedFilters.push({
|
|
2785
2817
|
field: {
|
|
@@ -2847,7 +2879,7 @@ async function convertEntityFiltersToRowFilters(routeContext, server, model, bas
|
|
|
2847
2879
|
return replacedFilters;
|
|
2848
2880
|
}
|
|
2849
2881
|
async function findManyRelationLinksViaLinkTable(options) {
|
|
2850
|
-
const { server, relationProperty, mainModel: relationModel, mainEntityIds, selectRelationOptions } = options;
|
|
2882
|
+
const { server, routeContext, relationProperty, mainModel: relationModel, mainEntityIds, selectRelationOptions } = options;
|
|
2851
2883
|
const command = `SELECT * FROM ${server.queryBuilder.quoteTable({
|
|
2852
2884
|
schema: relationProperty.linkSchema,
|
|
2853
2885
|
tableName: relationProperty.linkTableName,
|
|
@@ -2855,7 +2887,7 @@ async function findManyRelationLinksViaLinkTable(options) {
|
|
|
2855
2887
|
ORDER BY id
|
|
2856
2888
|
`;
|
|
2857
2889
|
const params = [mainEntityIds];
|
|
2858
|
-
const links = await server.queryDatabaseObject(command, params);
|
|
2890
|
+
const links = await server.queryDatabaseObject(command, params, routeContext?.getDbTransactionClient());
|
|
2859
2891
|
const targetEntityIds = links.map((link) => link[relationProperty.targetIdColumnName]);
|
|
2860
2892
|
const dataAccessor = server.getDataAccessor({
|
|
2861
2893
|
namespace: relationModel.namespace,
|
|
@@ -3157,7 +3189,7 @@ async function createEntity(server, dataAccessor, options, plugin) {
|
|
|
3157
3189
|
tableName: property.linkTableName,
|
|
3158
3190
|
})} (${server.queryBuilder.quoteObject(property.selfIdColumnName)}, ${property.targetIdColumnName}) VALUES ($1, $2) ON CONFLICT DO NOTHING;`;
|
|
3159
3191
|
const params = [newEntity.id, newTargetEntity.id];
|
|
3160
|
-
await server.queryDatabaseObject(command, params);
|
|
3192
|
+
await server.queryDatabaseObject(command, params, routeContext?.getDbTransactionClient());
|
|
3161
3193
|
}
|
|
3162
3194
|
newEntity[property.code].push(newTargetEntity);
|
|
3163
3195
|
}
|
|
@@ -3173,7 +3205,7 @@ async function createEntity(server, dataAccessor, options, plugin) {
|
|
|
3173
3205
|
tableName: property.linkTableName,
|
|
3174
3206
|
})} (${server.queryBuilder.quoteObject(property.selfIdColumnName)}, ${property.targetIdColumnName}) VALUES ($1, $2) ON CONFLICT DO NOTHING;`;
|
|
3175
3207
|
const params = [newEntity.id, relatedEntityId];
|
|
3176
|
-
await server.queryDatabaseObject(command, params);
|
|
3208
|
+
await server.queryDatabaseObject(command, params, routeContext?.getDbTransactionClient());
|
|
3177
3209
|
}
|
|
3178
3210
|
else {
|
|
3179
3211
|
await targetDataAccessor.updateById(targetEntity.id, { [property.selfIdColumnName]: newEntity.id }, routeContext?.getDbTransactionClient());
|
|
@@ -3195,7 +3227,7 @@ async function createEntity(server, dataAccessor, options, plugin) {
|
|
|
3195
3227
|
tableName: property.linkTableName,
|
|
3196
3228
|
})} (${server.queryBuilder.quoteObject(property.selfIdColumnName)}, ${property.targetIdColumnName}) VALUES ($1, $2) ON CONFLICT DO NOTHING;`;
|
|
3197
3229
|
const params = [newEntity.id, relatedEntityId];
|
|
3198
|
-
await server.queryDatabaseObject(command, params);
|
|
3230
|
+
await server.queryDatabaseObject(command, params, routeContext?.getDbTransactionClient());
|
|
3199
3231
|
}
|
|
3200
3232
|
else {
|
|
3201
3233
|
await targetDataAccessor.updateById(targetEntity.id, { [property.selfIdColumnName]: newEntity.id }, routeContext?.getDbTransactionClient());
|
|
@@ -3466,13 +3498,13 @@ async function updateEntityById(server, dataAccessor, options, plugin) {
|
|
|
3466
3498
|
const targetLinks = await server.queryDatabaseObject(`SELECT ${server.queryBuilder.quoteObject(property.targetIdColumnName)} FROM ${server.queryBuilder.quoteTable({
|
|
3467
3499
|
schema: property.linkSchema,
|
|
3468
3500
|
tableName: property.linkTableName,
|
|
3469
|
-
})} WHERE ${server.queryBuilder.quoteObject(property.selfIdColumnName)} = $1`, [id]);
|
|
3501
|
+
})} WHERE ${server.queryBuilder.quoteObject(property.selfIdColumnName)} = $1`, [id], routeContext?.getDbTransactionClient());
|
|
3470
3502
|
currentTargetIds = targetLinks.map((item) => item[property.targetIdColumnName]);
|
|
3471
3503
|
await server.queryDatabaseObject(`DELETE FROM ${server.queryBuilder.quoteTable({
|
|
3472
3504
|
schema: property.linkSchema,
|
|
3473
3505
|
tableName: property.linkTableName,
|
|
3474
3506
|
})} WHERE ${server.queryBuilder.quoteObject(property.selfIdColumnName)} = $1
|
|
3475
|
-
AND ${server.queryBuilder.quoteObject(property.targetIdColumnName)} <> ALL($2::int[])`, [id, targetIdsToKeep]);
|
|
3507
|
+
AND ${server.queryBuilder.quoteObject(property.targetIdColumnName)} <> ALL($2::int[])`, [id, targetIdsToKeep], routeContext?.getDbTransactionClient());
|
|
3476
3508
|
}
|
|
3477
3509
|
else {
|
|
3478
3510
|
const targetModel = server.getModel({
|
|
@@ -3481,7 +3513,7 @@ async function updateEntityById(server, dataAccessor, options, plugin) {
|
|
|
3481
3513
|
const targetRows = await server.queryDatabaseObject(`SELECT id FROM ${server.queryBuilder.quoteTable({
|
|
3482
3514
|
schema: targetModel.schema,
|
|
3483
3515
|
tableName: targetModel.tableName,
|
|
3484
|
-
})} WHERE ${server.queryBuilder.quoteObject(property.selfIdColumnName)} = $1`, [id]);
|
|
3516
|
+
})} WHERE ${server.queryBuilder.quoteObject(property.selfIdColumnName)} = $1`, [id], routeContext?.getDbTransactionClient());
|
|
3485
3517
|
currentTargetIds = targetRows.map((item) => item.id);
|
|
3486
3518
|
}
|
|
3487
3519
|
for (const relatedEntityToBeSaved of relatedEntitiesToBeSaved) {
|
|
@@ -3504,7 +3536,7 @@ async function updateEntityById(server, dataAccessor, options, plugin) {
|
|
|
3504
3536
|
tableName: property.linkTableName,
|
|
3505
3537
|
})} (${server.queryBuilder.quoteObject(property.selfIdColumnName)}, ${property.targetIdColumnName}) VALUES ($1, $2) ON CONFLICT DO NOTHING;`;
|
|
3506
3538
|
const params = [id, newTargetEntity.id];
|
|
3507
|
-
await server.queryDatabaseObject(command, params);
|
|
3539
|
+
await server.queryDatabaseObject(command, params, routeContext?.getDbTransactionClient());
|
|
3508
3540
|
}
|
|
3509
3541
|
relatedEntities.push(newTargetEntity);
|
|
3510
3542
|
}
|
|
@@ -3545,7 +3577,7 @@ async function updateEntityById(server, dataAccessor, options, plugin) {
|
|
|
3545
3577
|
tableName: property.linkTableName,
|
|
3546
3578
|
})} (${server.queryBuilder.quoteObject(property.selfIdColumnName)}, ${property.targetIdColumnName}) VALUES ($1, $2) ON CONFLICT DO NOTHING;`;
|
|
3547
3579
|
const params = [id, relatedEntityId];
|
|
3548
|
-
await server.queryDatabaseObject(command, params);
|
|
3580
|
+
await server.queryDatabaseObject(command, params, routeContext?.getDbTransactionClient());
|
|
3549
3581
|
}
|
|
3550
3582
|
else {
|
|
3551
3583
|
await targetDataAccessor.updateById(targetEntity.id, { [property.selfIdColumnName]: id }, routeContext?.getDbTransactionClient());
|
|
@@ -3569,7 +3601,7 @@ async function updateEntityById(server, dataAccessor, options, plugin) {
|
|
|
3569
3601
|
tableName: property.linkTableName,
|
|
3570
3602
|
})} (${server.queryBuilder.quoteObject(property.selfIdColumnName)}, ${property.targetIdColumnName}) VALUES ($1, $2) ON CONFLICT DO NOTHING;`;
|
|
3571
3603
|
const params = [id, relatedEntityId];
|
|
3572
|
-
await server.queryDatabaseObject(command, params);
|
|
3604
|
+
await server.queryDatabaseObject(command, params, routeContext?.getDbTransactionClient());
|
|
3573
3605
|
}
|
|
3574
3606
|
else {
|
|
3575
3607
|
await targetDataAccessor.updateById(targetEntity.id, { [property.selfIdColumnName]: id }, routeContext?.getDbTransactionClient());
|
|
@@ -3651,6 +3683,146 @@ function getEntityDuplicatedErrorMessage(server, model, indexConfig) {
|
|
|
3651
3683
|
});
|
|
3652
3684
|
return `已存在 ${propertyNames.join(", ")} 相同的记录。`;
|
|
3653
3685
|
}
|
|
3686
|
+
async function deleteEntityById(server, dataAccessor, options, plugin) {
|
|
3687
|
+
// options is id
|
|
3688
|
+
if (!lodash.isObject(options)) {
|
|
3689
|
+
options = {
|
|
3690
|
+
id: options,
|
|
3691
|
+
};
|
|
3692
|
+
}
|
|
3693
|
+
const model = dataAccessor.getModel();
|
|
3694
|
+
if (model.derivedTypePropertyCode) {
|
|
3695
|
+
// TODO: should be allowed.
|
|
3696
|
+
throw newEntityOperationError("Delete base entity directly is not allowed.");
|
|
3697
|
+
}
|
|
3698
|
+
const { id, routeContext } = options;
|
|
3699
|
+
const entity = await findById(server, dataAccessor, {
|
|
3700
|
+
id,
|
|
3701
|
+
keepNonPropertyFields: true,
|
|
3702
|
+
routeContext,
|
|
3703
|
+
});
|
|
3704
|
+
if (!entity) {
|
|
3705
|
+
return;
|
|
3706
|
+
}
|
|
3707
|
+
if (model.softDelete) {
|
|
3708
|
+
if (entity.deletedAt) {
|
|
3709
|
+
return;
|
|
3710
|
+
}
|
|
3711
|
+
}
|
|
3712
|
+
await server.emitEvent({
|
|
3713
|
+
eventName: "entity.beforeDelete",
|
|
3714
|
+
payload: {
|
|
3715
|
+
namespace: model.namespace,
|
|
3716
|
+
modelSingularCode: model.singularCode,
|
|
3717
|
+
before: entity,
|
|
3718
|
+
},
|
|
3719
|
+
sender: plugin,
|
|
3720
|
+
routeContext,
|
|
3721
|
+
});
|
|
3722
|
+
if (model.softDelete) {
|
|
3723
|
+
const currentUserId = routeContext?.state?.userId;
|
|
3724
|
+
await dataAccessor.updateById(id, {
|
|
3725
|
+
deleted_at: getNowStringWithTimezone(),
|
|
3726
|
+
deleter_id: currentUserId,
|
|
3727
|
+
}, routeContext?.getDbTransactionClient());
|
|
3728
|
+
}
|
|
3729
|
+
else {
|
|
3730
|
+
const relationPropertiesWithDeletingReaction = getEntityPropertiesIncludingBase(server, model, (property) => {
|
|
3731
|
+
return isRelationProperty(property) && property.entityDeletingReaction && property.entityDeletingReaction !== "doNothing";
|
|
3732
|
+
});
|
|
3733
|
+
for (const relationProperty of relationPropertiesWithDeletingReaction) {
|
|
3734
|
+
const relationDataAccessor = server.getDataAccessor({
|
|
3735
|
+
singularCode: relationProperty.targetSingularCode,
|
|
3736
|
+
});
|
|
3737
|
+
if (relationProperty.entityDeletingReaction === "cascadingDelete") {
|
|
3738
|
+
if (relationProperty.relation === "one") {
|
|
3739
|
+
const relatedEntityId = entity[relationProperty.targetIdColumnName];
|
|
3740
|
+
if (relatedEntityId) {
|
|
3741
|
+
await deleteEntityById(server, relationDataAccessor, {
|
|
3742
|
+
routeContext,
|
|
3743
|
+
id: relatedEntityId,
|
|
3744
|
+
}, plugin);
|
|
3745
|
+
}
|
|
3746
|
+
}
|
|
3747
|
+
else if (relationProperty.relation === "many") {
|
|
3748
|
+
if (relationProperty.linkTableName) {
|
|
3749
|
+
const targetLinks = await server.queryDatabaseObject(`SELECT ${server.queryBuilder.quoteObject(relationProperty.targetIdColumnName)} FROM ${server.queryBuilder.quoteTable({
|
|
3750
|
+
schema: relationProperty.linkSchema,
|
|
3751
|
+
tableName: relationProperty.linkTableName,
|
|
3752
|
+
})} WHERE ${server.queryBuilder.quoteObject(relationProperty.selfIdColumnName)} = $1`, [id], routeContext?.getDbTransactionClient());
|
|
3753
|
+
const targetEntityIds = targetLinks.map((item) => item[relationProperty.targetIdColumnName]);
|
|
3754
|
+
await server.queryDatabaseObject(`DELETE FROM ${server.queryBuilder.quoteTable({
|
|
3755
|
+
schema: relationProperty.linkSchema,
|
|
3756
|
+
tableName: relationProperty.linkTableName,
|
|
3757
|
+
})} WHERE ${server.queryBuilder.quoteObject(relationProperty.selfIdColumnName)} = $1`, [id], routeContext?.getDbTransactionClient());
|
|
3758
|
+
for (const targetEntityId of targetEntityIds) {
|
|
3759
|
+
await deleteEntityById(server, relationDataAccessor, {
|
|
3760
|
+
routeContext,
|
|
3761
|
+
id: targetEntityId,
|
|
3762
|
+
}, plugin);
|
|
3763
|
+
}
|
|
3764
|
+
}
|
|
3765
|
+
else {
|
|
3766
|
+
const targetModel = server.getModel({
|
|
3767
|
+
singularCode: relationProperty.targetSingularCode,
|
|
3768
|
+
});
|
|
3769
|
+
const targetRows = await server.queryDatabaseObject(`SELECT id FROM ${server.queryBuilder.quoteTable({
|
|
3770
|
+
schema: targetModel.schema,
|
|
3771
|
+
tableName: targetModel.tableName,
|
|
3772
|
+
})} WHERE ${server.queryBuilder.quoteObject(relationProperty.selfIdColumnName)} = $1`, [id], routeContext?.getDbTransactionClient());
|
|
3773
|
+
const targetEntityIds = targetRows.map((item) => item.id);
|
|
3774
|
+
for (const targetEntityId of targetEntityIds) {
|
|
3775
|
+
await deleteEntityById(server, relationDataAccessor, {
|
|
3776
|
+
routeContext,
|
|
3777
|
+
id: targetEntityId,
|
|
3778
|
+
}, plugin);
|
|
3779
|
+
}
|
|
3780
|
+
}
|
|
3781
|
+
}
|
|
3782
|
+
}
|
|
3783
|
+
else if (relationProperty.entityDeletingReaction === "unlink") {
|
|
3784
|
+
if (relationProperty.relation === "one") ;
|
|
3785
|
+
else if (relationProperty.relation === "many") {
|
|
3786
|
+
if (relationProperty.linkTableName) {
|
|
3787
|
+
await server.queryDatabaseObject(`DELETE FROM ${server.queryBuilder.quoteTable({
|
|
3788
|
+
schema: relationProperty.linkSchema,
|
|
3789
|
+
tableName: relationProperty.linkTableName,
|
|
3790
|
+
})}
|
|
3791
|
+
WHERE ${server.queryBuilder.quoteObject(relationProperty.selfIdColumnName)} = $1`, [id], routeContext?.getDbTransactionClient());
|
|
3792
|
+
}
|
|
3793
|
+
else {
|
|
3794
|
+
const relationModel = server.getModel({
|
|
3795
|
+
singularCode: relationProperty.targetSingularCode,
|
|
3796
|
+
});
|
|
3797
|
+
await server.queryDatabaseObject(`UPDATE ${server.queryBuilder.quoteTable({
|
|
3798
|
+
schema: relationModel.schema,
|
|
3799
|
+
tableName: relationModel.tableName,
|
|
3800
|
+
})}
|
|
3801
|
+
SET ${server.queryBuilder.quoteObject(relationProperty.selfIdColumnName)} = null
|
|
3802
|
+
WHERE ${server.queryBuilder.quoteObject(relationProperty.selfIdColumnName)} = $1`, [id], routeContext?.getDbTransactionClient());
|
|
3803
|
+
}
|
|
3804
|
+
}
|
|
3805
|
+
}
|
|
3806
|
+
}
|
|
3807
|
+
await dataAccessor.deleteById(id, routeContext?.getDbTransactionClient());
|
|
3808
|
+
if (model.base) {
|
|
3809
|
+
const baseDataAccessor = server.getDataAccessor({
|
|
3810
|
+
singularCode: model.base,
|
|
3811
|
+
});
|
|
3812
|
+
await baseDataAccessor.deleteById(id, routeContext?.getDbTransactionClient());
|
|
3813
|
+
}
|
|
3814
|
+
}
|
|
3815
|
+
await server.emitEvent({
|
|
3816
|
+
eventName: "entity.delete",
|
|
3817
|
+
payload: {
|
|
3818
|
+
namespace: model.namespace,
|
|
3819
|
+
modelSingularCode: model.singularCode,
|
|
3820
|
+
before: entity,
|
|
3821
|
+
},
|
|
3822
|
+
sender: plugin,
|
|
3823
|
+
routeContext,
|
|
3824
|
+
});
|
|
3825
|
+
}
|
|
3654
3826
|
class EntityManager {
|
|
3655
3827
|
#server;
|
|
3656
3828
|
#dataAccessor;
|
|
@@ -3697,66 +3869,7 @@ class EntityManager {
|
|
|
3697
3869
|
return await this.#dataAccessor.count(countRowOptions, routeContext?.getDbTransactionClient());
|
|
3698
3870
|
}
|
|
3699
3871
|
async deleteById(options, plugin) {
|
|
3700
|
-
|
|
3701
|
-
if (!lodash.isObject(options)) {
|
|
3702
|
-
options = {
|
|
3703
|
-
id: options,
|
|
3704
|
-
};
|
|
3705
|
-
}
|
|
3706
|
-
const model = this.getModel();
|
|
3707
|
-
if (model.derivedTypePropertyCode) {
|
|
3708
|
-
throw newEntityOperationError("Delete base entity directly is not allowed.");
|
|
3709
|
-
}
|
|
3710
|
-
const { id, routeContext } = options;
|
|
3711
|
-
const entity = await this.findById({
|
|
3712
|
-
id,
|
|
3713
|
-
keepNonPropertyFields: true,
|
|
3714
|
-
routeContext,
|
|
3715
|
-
});
|
|
3716
|
-
if (!entity) {
|
|
3717
|
-
return;
|
|
3718
|
-
}
|
|
3719
|
-
await this.#server.emitEvent({
|
|
3720
|
-
eventName: "entity.beforeDelete",
|
|
3721
|
-
payload: {
|
|
3722
|
-
namespace: model.namespace,
|
|
3723
|
-
modelSingularCode: model.singularCode,
|
|
3724
|
-
before: entity,
|
|
3725
|
-
},
|
|
3726
|
-
sender: plugin,
|
|
3727
|
-
routeContext,
|
|
3728
|
-
});
|
|
3729
|
-
if (model.softDelete) {
|
|
3730
|
-
let dataAccessor = model.base
|
|
3731
|
-
? this.#server.getDataAccessor({
|
|
3732
|
-
singularCode: model.base,
|
|
3733
|
-
})
|
|
3734
|
-
: this.#dataAccessor;
|
|
3735
|
-
const currentUserId = routeContext?.state?.userId;
|
|
3736
|
-
await dataAccessor.updateById(id, {
|
|
3737
|
-
deleted_at: getNowStringWithTimezone(),
|
|
3738
|
-
deleter_id: currentUserId,
|
|
3739
|
-
}, routeContext?.getDbTransactionClient());
|
|
3740
|
-
}
|
|
3741
|
-
else {
|
|
3742
|
-
await this.#dataAccessor.deleteById(id, routeContext?.getDbTransactionClient());
|
|
3743
|
-
if (model.base) {
|
|
3744
|
-
const baseDataAccessor = this.#server.getDataAccessor({
|
|
3745
|
-
singularCode: model.base,
|
|
3746
|
-
});
|
|
3747
|
-
await baseDataAccessor.deleteById(id, routeContext?.getDbTransactionClient());
|
|
3748
|
-
}
|
|
3749
|
-
}
|
|
3750
|
-
await this.#server.emitEvent({
|
|
3751
|
-
eventName: "entity.delete",
|
|
3752
|
-
payload: {
|
|
3753
|
-
namespace: model.namespace,
|
|
3754
|
-
modelSingularCode: model.singularCode,
|
|
3755
|
-
before: entity,
|
|
3756
|
-
},
|
|
3757
|
-
sender: plugin,
|
|
3758
|
-
routeContext,
|
|
3759
|
-
});
|
|
3872
|
+
return await deleteEntityById(this.#server, this.#dataAccessor, options, plugin);
|
|
3760
3873
|
}
|
|
3761
3874
|
async addRelations(options, plugin) {
|
|
3762
3875
|
const server = this.#server;
|
|
@@ -3789,7 +3902,7 @@ class EntityManager {
|
|
|
3789
3902
|
WHERE ${queryBuilder.quoteObject(relationProperty.selfIdColumnName)}=$1 AND ${queryBuilder.quoteObject(relationProperty.targetIdColumnName)}=$2
|
|
3790
3903
|
)`;
|
|
3791
3904
|
const params = [id, relation.id];
|
|
3792
|
-
await server.queryDatabaseObject(command, params);
|
|
3905
|
+
await server.queryDatabaseObject(command, params, routeContext?.getDbTransactionClient());
|
|
3793
3906
|
}
|
|
3794
3907
|
}
|
|
3795
3908
|
await server.emitEvent({
|
|
@@ -3829,7 +3942,7 @@ class EntityManager {
|
|
|
3829
3942
|
const command = `DELETE FROM ${queryBuilder.quoteTable({ schema: relationProperty.linkSchema, tableName: relationProperty.linkTableName })}
|
|
3830
3943
|
WHERE ${queryBuilder.quoteObject(relationProperty.selfIdColumnName)}=$1 AND ${queryBuilder.quoteObject(relationProperty.targetIdColumnName)}=$2;`;
|
|
3831
3944
|
const params = [id, relation.id];
|
|
3832
|
-
await server.queryDatabaseObject(command, params);
|
|
3945
|
+
await server.queryDatabaseObject(command, params, routeContext?.getDbTransactionClient());
|
|
3833
3946
|
}
|
|
3834
3947
|
}
|
|
3835
3948
|
await server.emitEvent({
|
|
@@ -4136,18 +4249,18 @@ class RapidServer {
|
|
|
4136
4249
|
}
|
|
4137
4250
|
return await factory.createFacility(this, options);
|
|
4138
4251
|
}
|
|
4139
|
-
async queryDatabaseObject(sql, params) {
|
|
4252
|
+
async queryDatabaseObject(sql, params, client) {
|
|
4140
4253
|
try {
|
|
4141
|
-
return await this.#databaseAccessor.queryDatabaseObject(sql, params);
|
|
4254
|
+
return await this.#databaseAccessor.queryDatabaseObject(sql, params, client);
|
|
4142
4255
|
}
|
|
4143
4256
|
catch (err) {
|
|
4144
4257
|
this.#logger.error("Failed to query database object.", { errorMessage: err.message, sql, params });
|
|
4145
4258
|
throw err;
|
|
4146
4259
|
}
|
|
4147
4260
|
}
|
|
4148
|
-
async tryQueryDatabaseObject(sql, params) {
|
|
4261
|
+
async tryQueryDatabaseObject(sql, params, client) {
|
|
4149
4262
|
try {
|
|
4150
|
-
return await this.queryDatabaseObject(sql, params);
|
|
4263
|
+
return await this.queryDatabaseObject(sql, params, client);
|
|
4151
4264
|
}
|
|
4152
4265
|
catch (err) {
|
|
4153
4266
|
this.#logger.error("Failed to query database object.", { errorMessage: err.message, sql, params });
|
|
@@ -4161,19 +4274,27 @@ class RapidServer {
|
|
|
4161
4274
|
const rapidRequest = new RapidRequest(this, request);
|
|
4162
4275
|
await rapidRequest.parseBody();
|
|
4163
4276
|
const routeContext = new RouteContext(this, rapidRequest);
|
|
4277
|
+
const { response } = routeContext;
|
|
4164
4278
|
try {
|
|
4165
4279
|
await this.#pluginManager.onPrepareRouteContext(routeContext);
|
|
4166
4280
|
await this.#buildedRoutes(routeContext, next);
|
|
4167
4281
|
}
|
|
4168
4282
|
catch (ex) {
|
|
4169
4283
|
this.#logger.error("handle request error:", ex);
|
|
4170
|
-
|
|
4284
|
+
response.json({
|
|
4171
4285
|
error: {
|
|
4172
4286
|
message: ex.message || ex,
|
|
4173
4287
|
},
|
|
4174
4288
|
}, 500);
|
|
4175
4289
|
}
|
|
4176
|
-
|
|
4290
|
+
if (!response.status && !response.body) {
|
|
4291
|
+
response.json({
|
|
4292
|
+
error: {
|
|
4293
|
+
message: "No route handler was found to handle this request.",
|
|
4294
|
+
},
|
|
4295
|
+
}, 404);
|
|
4296
|
+
}
|
|
4297
|
+
return response.getResponse();
|
|
4177
4298
|
}
|
|
4178
4299
|
async beforeRunRouteActions(handlerContext) {
|
|
4179
4300
|
await this.#pluginManager.beforeRunRouteActions(handlerContext);
|
|
@@ -4989,7 +5110,13 @@ async function handler$q(plugin, ctx, options) {
|
|
|
4989
5110
|
routeContext,
|
|
4990
5111
|
});
|
|
4991
5112
|
if (!entity) {
|
|
4992
|
-
|
|
5113
|
+
ctx.routerContext.response.json({
|
|
5114
|
+
error: {
|
|
5115
|
+
message: `${options.namespace}.${options.singularCode} with id "${id}" was not found.`,
|
|
5116
|
+
},
|
|
5117
|
+
}, 404);
|
|
5118
|
+
// routerContext.json() function will not be called if the ctx.output is null or undefined.
|
|
5119
|
+
return;
|
|
4993
5120
|
}
|
|
4994
5121
|
return entity;
|
|
4995
5122
|
});
|
package/dist/server.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { GetDataAccessorOptions, GetModelOptions, IDatabaseAccessor, IDatabaseConfig, IQueryBuilder, IRpdDataAccessor, RpdApplicationConfig, RpdDataModel, RpdServerEventTypes, RapidServerConfig, RpdDataModelProperty, CreateEntityOptions, UpdateEntityByIdOptions, EntityWatcherType, EmitServerEventOptions } from "./types";
|
|
1
|
+
import { GetDataAccessorOptions, GetModelOptions, IDatabaseAccessor, IDatabaseConfig, IQueryBuilder, IRpdDataAccessor, RpdApplicationConfig, RpdDataModel, RpdServerEventTypes, RapidServerConfig, RpdDataModelProperty, CreateEntityOptions, UpdateEntityByIdOptions, EntityWatcherType, EmitServerEventOptions, IDatabaseClient } from "./types";
|
|
2
2
|
import { ActionHandler, ActionHandlerContext, IPluginActionHandler } from "./core/actionHandler";
|
|
3
3
|
import { IRpdServer, RapidPlugin } from "./core/server";
|
|
4
4
|
import { Next } from "./core/routeContext";
|
|
@@ -41,8 +41,8 @@ export declare class RapidServer implements IRpdServer {
|
|
|
41
41
|
configureApplication(): Promise<void>;
|
|
42
42
|
registerFacilityFactory(factory: FacilityFactory): void;
|
|
43
43
|
getFacility<TFacility = any>(name: string, options?: any, nullIfUnknownFacility?: boolean): Promise<TFacility>;
|
|
44
|
-
queryDatabaseObject(sql: string, params?: unknown[] | Record<string, unknown
|
|
45
|
-
tryQueryDatabaseObject(sql: string, params?: unknown[] | Record<string, unknown
|
|
44
|
+
queryDatabaseObject(sql: string, params?: unknown[] | Record<string, unknown>, client?: IDatabaseClient): Promise<any[]>;
|
|
45
|
+
tryQueryDatabaseObject(sql: string, params?: unknown[] | Record<string, unknown>, client?: IDatabaseClient): Promise<any[]>;
|
|
46
46
|
get middlewares(): any[];
|
|
47
47
|
handleRequest(request: Request, next: Next): Promise<Response>;
|
|
48
48
|
beforeRunRouteActions(handlerContext: ActionHandlerContext): Promise<void>;
|
package/dist/types.d.ts
CHANGED
|
@@ -282,12 +282,17 @@ export interface RpdDataModelProperty {
|
|
|
282
282
|
* 当设置了 linkTableName 时,可以设置关联关系表所在的 Schema。
|
|
283
283
|
*/
|
|
284
284
|
linkSchema?: string;
|
|
285
|
+
/**
|
|
286
|
+
* 当删除实体时,针对关系属性的联动处理。
|
|
287
|
+
*/
|
|
288
|
+
entityDeletingReaction?: RpdEntityDeleteRelationPropertyReaction;
|
|
285
289
|
/**
|
|
286
290
|
* 当设置为`true`时,仅允许在创建时设置此属性的值,不允许更新。
|
|
287
291
|
*/
|
|
288
292
|
readonly?: boolean;
|
|
289
293
|
}
|
|
290
294
|
export type RpdDataPropertyTypes = "integer" | "long" | "float" | "double" | "decimal" | "text" | "boolean" | "date" | "time" | "datetime" | "json" | "relation" | "relation[]" | "option" | "option[]" | "file" | "file[]" | "image" | "image[]";
|
|
295
|
+
export type RpdEntityDeleteRelationPropertyReaction = "doNothing" | "unlink" | "cascadingDelete";
|
|
291
296
|
/**
|
|
292
297
|
* 数据字典
|
|
293
298
|
*/
|
package/package.json
CHANGED
|
@@ -237,6 +237,13 @@ export default {
|
|
|
237
237
|
type: "text",
|
|
238
238
|
required: false,
|
|
239
239
|
},
|
|
240
|
+
{
|
|
241
|
+
name: "entityDeletingReaction",
|
|
242
|
+
code: "entityDeletingReaction",
|
|
243
|
+
columnName: "entity_deleting_reaction",
|
|
244
|
+
type: "text",
|
|
245
|
+
required: false,
|
|
246
|
+
},
|
|
240
247
|
{
|
|
241
248
|
name: "readonly",
|
|
242
249
|
code: "readonly",
|
package/src/core/response.ts
CHANGED
|
@@ -15,7 +15,6 @@ function mergeHeaders(target: Headers, source: HeadersInit) {
|
|
|
15
15
|
} else if (isObject(source)) {
|
|
16
16
|
Object.entries(source).forEach(([key, value]) => target.set(key, value));
|
|
17
17
|
}
|
|
18
|
-
return target;
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
interface NewResponseOptions {
|
|
@@ -32,6 +31,7 @@ function newResponse(options: NewResponseOptions) {
|
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
export class RapidResponse {
|
|
34
|
+
// TODO: remove this field.
|
|
35
35
|
#response: Response;
|
|
36
36
|
status: number;
|
|
37
37
|
body: BodyInit;
|
|
@@ -53,14 +53,17 @@ export class RapidResponse {
|
|
|
53
53
|
if (headers) {
|
|
54
54
|
mergeHeaders(responseHeaders, headers);
|
|
55
55
|
}
|
|
56
|
-
this
|
|
56
|
+
this.status = status || 200;
|
|
57
|
+
this.body = body;
|
|
58
|
+
this.#response = newResponse({ body, status: this.status, headers: responseHeaders });
|
|
57
59
|
}
|
|
58
60
|
|
|
59
61
|
redirect(location: string, status?: HttpStatus) {
|
|
60
62
|
this.headers.set("Location", location);
|
|
63
|
+
this.status = status || 302;
|
|
61
64
|
this.#response = newResponse({
|
|
62
65
|
headers: this.headers,
|
|
63
|
-
status: status
|
|
66
|
+
status: this.status,
|
|
64
67
|
});
|
|
65
68
|
}
|
|
66
69
|
|
package/src/core/server.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
GetDataAccessorOptions,
|
|
6
6
|
GetModelOptions,
|
|
7
7
|
IDatabaseAccessor,
|
|
8
|
+
IDatabaseClient,
|
|
8
9
|
IDatabaseConfig,
|
|
9
10
|
IQueryBuilder,
|
|
10
11
|
IRpdDataAccessor,
|
|
@@ -32,8 +33,8 @@ export interface IRpdServer {
|
|
|
32
33
|
getFacility<TFacility = any>(name: string, options?: any): Promise<TFacility>;
|
|
33
34
|
|
|
34
35
|
getDatabaseAccessor(): IDatabaseAccessor;
|
|
35
|
-
queryDatabaseObject: (sql: string, params?: unknown[] | Record<string, unknown
|
|
36
|
-
tryQueryDatabaseObject: (sql: string, params?: unknown[] | Record<string, unknown
|
|
36
|
+
queryDatabaseObject: (sql: string, params?: unknown[] | Record<string, unknown>, client?: IDatabaseClient) => Promise<any[]>;
|
|
37
|
+
tryQueryDatabaseObject: (sql: string, params?: unknown[] | Record<string, unknown>, client?: IDatabaseClient) => Promise<any[]>;
|
|
37
38
|
registerMiddleware(middleware: any): void;
|
|
38
39
|
registerActionHandler(plugin: RapidPlugin, options: IPluginActionHandler): void;
|
|
39
40
|
getActionHandlerByCode(code: string): ActionHandler | undefined;
|
|
@@ -45,6 +45,7 @@ import {
|
|
|
45
45
|
uniq,
|
|
46
46
|
} from "lodash";
|
|
47
47
|
import {
|
|
48
|
+
getEntityProperties,
|
|
48
49
|
getEntityPropertiesIncludingBase,
|
|
49
50
|
getEntityProperty,
|
|
50
51
|
getEntityPropertyByCode,
|
|
@@ -60,6 +61,7 @@ import { RouteContext } from "~/core/routeContext";
|
|
|
60
61
|
|
|
61
62
|
export type FindOneRelationEntitiesOptions = {
|
|
62
63
|
server: IRpdServer;
|
|
64
|
+
routeContext?: RouteContext;
|
|
63
65
|
mainModel: RpdDataModel;
|
|
64
66
|
relationProperty: RpdDataModelProperty;
|
|
65
67
|
relationEntityIds: any[];
|
|
@@ -68,6 +70,7 @@ export type FindOneRelationEntitiesOptions = {
|
|
|
68
70
|
|
|
69
71
|
export type FindManyRelationEntitiesOptions = {
|
|
70
72
|
server: IRpdServer;
|
|
73
|
+
routeContext?: RouteContext;
|
|
71
74
|
mainModel: RpdDataModel;
|
|
72
75
|
relationProperty: RpdDataModelProperty;
|
|
73
76
|
mainEntityIds: any[];
|
|
@@ -269,6 +272,7 @@ async function findEntities(server: IRpdServer, dataAccessor: IRpdDataAccessor,
|
|
|
269
272
|
if (isManyRelation) {
|
|
270
273
|
const relationLinks = await findManyRelationLinksViaLinkTable({
|
|
271
274
|
server,
|
|
275
|
+
routeContext,
|
|
272
276
|
mainModel: relationModel,
|
|
273
277
|
relationProperty,
|
|
274
278
|
mainEntityIds: entityIds,
|
|
@@ -286,6 +290,7 @@ async function findEntities(server: IRpdServer, dataAccessor: IRpdDataAccessor,
|
|
|
286
290
|
if (isManyRelation) {
|
|
287
291
|
relatedEntities = await findManyRelatedEntitiesViaIdPropertyCode({
|
|
288
292
|
server,
|
|
293
|
+
routeContext,
|
|
289
294
|
mainModel: model,
|
|
290
295
|
relationProperty,
|
|
291
296
|
mainEntityIds: entityIds,
|
|
@@ -300,6 +305,7 @@ async function findEntities(server: IRpdServer, dataAccessor: IRpdDataAccessor,
|
|
|
300
305
|
);
|
|
301
306
|
relatedEntities = await findOneRelatedEntitiesViaIdPropertyCode({
|
|
302
307
|
server,
|
|
308
|
+
routeContext,
|
|
303
309
|
mainModel: model,
|
|
304
310
|
relationProperty,
|
|
305
311
|
relationEntityIds: targetEntityIds,
|
|
@@ -546,7 +552,7 @@ async function convertEntityFiltersToRowFilters(
|
|
|
546
552
|
tableName: relationProperty.linkTableName!,
|
|
547
553
|
})} WHERE ${server.queryBuilder.quoteObject(relationProperty.targetIdColumnName!)} = ANY($1::int[])`;
|
|
548
554
|
const params = [targetEntityIds];
|
|
549
|
-
const links = await server.queryDatabaseObject(command, params);
|
|
555
|
+
const links = await server.queryDatabaseObject(command, params, routeContext?.getDbTransactionClient());
|
|
550
556
|
const selfEntityIds = links.map((link) => link[relationProperty.selfIdColumnName!]);
|
|
551
557
|
replacedFilters.push({
|
|
552
558
|
field: {
|
|
@@ -614,7 +620,7 @@ async function convertEntityFiltersToRowFilters(
|
|
|
614
620
|
}
|
|
615
621
|
|
|
616
622
|
async function findManyRelationLinksViaLinkTable(options: FindManyRelationEntitiesOptions) {
|
|
617
|
-
const { server, relationProperty, mainModel: relationModel, mainEntityIds, selectRelationOptions } = options;
|
|
623
|
+
const { server, routeContext, relationProperty, mainModel: relationModel, mainEntityIds, selectRelationOptions } = options;
|
|
618
624
|
const command = `SELECT * FROM ${server.queryBuilder.quoteTable({
|
|
619
625
|
schema: relationProperty.linkSchema,
|
|
620
626
|
tableName: relationProperty.linkTableName!,
|
|
@@ -622,7 +628,7 @@ async function findManyRelationLinksViaLinkTable(options: FindManyRelationEntiti
|
|
|
622
628
|
ORDER BY id
|
|
623
629
|
`;
|
|
624
630
|
const params = [mainEntityIds];
|
|
625
|
-
const links = await server.queryDatabaseObject(command, params);
|
|
631
|
+
const links = await server.queryDatabaseObject(command, params, routeContext?.getDbTransactionClient());
|
|
626
632
|
const targetEntityIds = links.map((link) => link[relationProperty.targetIdColumnName!]);
|
|
627
633
|
|
|
628
634
|
const dataAccessor = server.getDataAccessor({
|
|
@@ -960,7 +966,7 @@ async function createEntity(server: IRpdServer, dataAccessor: IRpdDataAccessor,
|
|
|
960
966
|
tableName: property.linkTableName,
|
|
961
967
|
})} (${server.queryBuilder.quoteObject(property.selfIdColumnName!)}, ${property.targetIdColumnName}) VALUES ($1, $2) ON CONFLICT DO NOTHING;`;
|
|
962
968
|
const params = [newEntity.id, newTargetEntity.id];
|
|
963
|
-
await server.queryDatabaseObject(command, params);
|
|
969
|
+
await server.queryDatabaseObject(command, params, routeContext?.getDbTransactionClient());
|
|
964
970
|
}
|
|
965
971
|
|
|
966
972
|
newEntity[property.code].push(newTargetEntity);
|
|
@@ -977,7 +983,7 @@ async function createEntity(server: IRpdServer, dataAccessor: IRpdDataAccessor,
|
|
|
977
983
|
tableName: property.linkTableName,
|
|
978
984
|
})} (${server.queryBuilder.quoteObject(property.selfIdColumnName!)}, ${property.targetIdColumnName}) VALUES ($1, $2) ON CONFLICT DO NOTHING;`;
|
|
979
985
|
const params = [newEntity.id, relatedEntityId];
|
|
980
|
-
await server.queryDatabaseObject(command, params);
|
|
986
|
+
await server.queryDatabaseObject(command, params, routeContext?.getDbTransactionClient());
|
|
981
987
|
} else {
|
|
982
988
|
await targetDataAccessor.updateById(targetEntity.id, { [property.selfIdColumnName!]: newEntity.id }, routeContext?.getDbTransactionClient());
|
|
983
989
|
targetEntity[property.selfIdColumnName!] = newEntity.id;
|
|
@@ -998,7 +1004,7 @@ async function createEntity(server: IRpdServer, dataAccessor: IRpdDataAccessor,
|
|
|
998
1004
|
tableName: property.linkTableName,
|
|
999
1005
|
})} (${server.queryBuilder.quoteObject(property.selfIdColumnName!)}, ${property.targetIdColumnName}) VALUES ($1, $2) ON CONFLICT DO NOTHING;`;
|
|
1000
1006
|
const params = [newEntity.id, relatedEntityId];
|
|
1001
|
-
await server.queryDatabaseObject(command, params);
|
|
1007
|
+
await server.queryDatabaseObject(command, params, routeContext?.getDbTransactionClient());
|
|
1002
1008
|
} else {
|
|
1003
1009
|
await targetDataAccessor.updateById(targetEntity.id, { [property.selfIdColumnName!]: newEntity.id }, routeContext?.getDbTransactionClient());
|
|
1004
1010
|
targetEntity[property.selfIdColumnName!] = newEntity.id;
|
|
@@ -1298,6 +1304,7 @@ async function updateEntityById(server: IRpdServer, dataAccessor: IRpdDataAccess
|
|
|
1298
1304
|
tableName: property.linkTableName,
|
|
1299
1305
|
})} WHERE ${server.queryBuilder.quoteObject(property.selfIdColumnName!)} = $1`,
|
|
1300
1306
|
[id],
|
|
1307
|
+
routeContext?.getDbTransactionClient(),
|
|
1301
1308
|
);
|
|
1302
1309
|
currentTargetIds = targetLinks.map((item) => item[property.targetIdColumnName]);
|
|
1303
1310
|
|
|
@@ -1308,6 +1315,7 @@ async function updateEntityById(server: IRpdServer, dataAccessor: IRpdDataAccess
|
|
|
1308
1315
|
})} WHERE ${server.queryBuilder.quoteObject(property.selfIdColumnName!)} = $1
|
|
1309
1316
|
AND ${server.queryBuilder.quoteObject(property.targetIdColumnName!)} <> ALL($2::int[])`,
|
|
1310
1317
|
[id, targetIdsToKeep],
|
|
1318
|
+
routeContext?.getDbTransactionClient(),
|
|
1311
1319
|
);
|
|
1312
1320
|
} else {
|
|
1313
1321
|
const targetModel = server.getModel({
|
|
@@ -1319,6 +1327,7 @@ async function updateEntityById(server: IRpdServer, dataAccessor: IRpdDataAccess
|
|
|
1319
1327
|
tableName: targetModel.tableName,
|
|
1320
1328
|
})} WHERE ${server.queryBuilder.quoteObject(property.selfIdColumnName!)} = $1`,
|
|
1321
1329
|
[id],
|
|
1330
|
+
routeContext?.getDbTransactionClient(),
|
|
1322
1331
|
);
|
|
1323
1332
|
currentTargetIds = targetRows.map((item) => item.id);
|
|
1324
1333
|
}
|
|
@@ -1344,7 +1353,7 @@ async function updateEntityById(server: IRpdServer, dataAccessor: IRpdDataAccess
|
|
|
1344
1353
|
tableName: property.linkTableName,
|
|
1345
1354
|
})} (${server.queryBuilder.quoteObject(property.selfIdColumnName!)}, ${property.targetIdColumnName}) VALUES ($1, $2) ON CONFLICT DO NOTHING;`;
|
|
1346
1355
|
const params = [id, newTargetEntity.id];
|
|
1347
|
-
await server.queryDatabaseObject(command, params);
|
|
1356
|
+
await server.queryDatabaseObject(command, params, routeContext?.getDbTransactionClient());
|
|
1348
1357
|
}
|
|
1349
1358
|
|
|
1350
1359
|
relatedEntities.push(newTargetEntity);
|
|
@@ -1386,7 +1395,7 @@ async function updateEntityById(server: IRpdServer, dataAccessor: IRpdDataAccess
|
|
|
1386
1395
|
tableName: property.linkTableName,
|
|
1387
1396
|
})} (${server.queryBuilder.quoteObject(property.selfIdColumnName!)}, ${property.targetIdColumnName}) VALUES ($1, $2) ON CONFLICT DO NOTHING;`;
|
|
1388
1397
|
const params = [id, relatedEntityId];
|
|
1389
|
-
await server.queryDatabaseObject(command, params);
|
|
1398
|
+
await server.queryDatabaseObject(command, params, routeContext?.getDbTransactionClient());
|
|
1390
1399
|
} else {
|
|
1391
1400
|
await targetDataAccessor.updateById(targetEntity.id, { [property.selfIdColumnName!]: id }, routeContext?.getDbTransactionClient());
|
|
1392
1401
|
targetEntity[property.selfIdColumnName!] = id;
|
|
@@ -1409,7 +1418,7 @@ async function updateEntityById(server: IRpdServer, dataAccessor: IRpdDataAccess
|
|
|
1409
1418
|
tableName: property.linkTableName,
|
|
1410
1419
|
})} (${server.queryBuilder.quoteObject(property.selfIdColumnName!)}, ${property.targetIdColumnName}) VALUES ($1, $2) ON CONFLICT DO NOTHING;`;
|
|
1411
1420
|
const params = [id, relatedEntityId];
|
|
1412
|
-
await server.queryDatabaseObject(command, params);
|
|
1421
|
+
await server.queryDatabaseObject(command, params, routeContext?.getDbTransactionClient());
|
|
1413
1422
|
} else {
|
|
1414
1423
|
await targetDataAccessor.updateById(targetEntity.id, { [property.selfIdColumnName!]: id }, routeContext?.getDbTransactionClient());
|
|
1415
1424
|
targetEntity[property.selfIdColumnName!] = id;
|
|
@@ -1508,6 +1517,199 @@ function getEntityDuplicatedErrorMessage(server: IRpdServer, model: RpdDataModel
|
|
|
1508
1517
|
return `已存在 ${propertyNames.join(", ")} 相同的记录。`;
|
|
1509
1518
|
}
|
|
1510
1519
|
|
|
1520
|
+
async function deleteEntityById(
|
|
1521
|
+
server: IRpdServer,
|
|
1522
|
+
dataAccessor: IRpdDataAccessor,
|
|
1523
|
+
options: DeleteEntityByIdOptions | string | number,
|
|
1524
|
+
plugin?: RapidPlugin,
|
|
1525
|
+
): Promise<void> {
|
|
1526
|
+
// options is id
|
|
1527
|
+
if (!isObject(options)) {
|
|
1528
|
+
options = {
|
|
1529
|
+
id: options,
|
|
1530
|
+
};
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
const model = dataAccessor.getModel();
|
|
1534
|
+
if (model.derivedTypePropertyCode) {
|
|
1535
|
+
// TODO: should be allowed.
|
|
1536
|
+
throw newEntityOperationError("Delete base entity directly is not allowed.");
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
const { id, routeContext } = options;
|
|
1540
|
+
|
|
1541
|
+
const entity = await findById(server, dataAccessor, {
|
|
1542
|
+
id,
|
|
1543
|
+
keepNonPropertyFields: true,
|
|
1544
|
+
routeContext,
|
|
1545
|
+
});
|
|
1546
|
+
|
|
1547
|
+
if (!entity) {
|
|
1548
|
+
return;
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
if (model.softDelete) {
|
|
1552
|
+
if (entity.deletedAt) {
|
|
1553
|
+
return;
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
await server.emitEvent({
|
|
1558
|
+
eventName: "entity.beforeDelete",
|
|
1559
|
+
payload: {
|
|
1560
|
+
namespace: model.namespace,
|
|
1561
|
+
modelSingularCode: model.singularCode,
|
|
1562
|
+
before: entity,
|
|
1563
|
+
},
|
|
1564
|
+
sender: plugin,
|
|
1565
|
+
routeContext,
|
|
1566
|
+
});
|
|
1567
|
+
|
|
1568
|
+
if (model.softDelete) {
|
|
1569
|
+
const currentUserId = routeContext?.state?.userId;
|
|
1570
|
+
await dataAccessor.updateById(
|
|
1571
|
+
id,
|
|
1572
|
+
{
|
|
1573
|
+
deleted_at: getNowStringWithTimezone(),
|
|
1574
|
+
deleter_id: currentUserId,
|
|
1575
|
+
},
|
|
1576
|
+
routeContext?.getDbTransactionClient(),
|
|
1577
|
+
);
|
|
1578
|
+
} else {
|
|
1579
|
+
const relationPropertiesWithDeletingReaction = getEntityPropertiesIncludingBase(server, model, (property) => {
|
|
1580
|
+
return isRelationProperty(property) && property.entityDeletingReaction && property.entityDeletingReaction !== "doNothing";
|
|
1581
|
+
});
|
|
1582
|
+
|
|
1583
|
+
for (const relationProperty of relationPropertiesWithDeletingReaction) {
|
|
1584
|
+
const relationDataAccessor = server.getDataAccessor({
|
|
1585
|
+
singularCode: relationProperty.targetSingularCode,
|
|
1586
|
+
});
|
|
1587
|
+
if (relationProperty.entityDeletingReaction === "cascadingDelete") {
|
|
1588
|
+
if (relationProperty.relation === "one") {
|
|
1589
|
+
const relatedEntityId = entity[relationProperty.targetIdColumnName];
|
|
1590
|
+
if (relatedEntityId) {
|
|
1591
|
+
await deleteEntityById(
|
|
1592
|
+
server,
|
|
1593
|
+
relationDataAccessor,
|
|
1594
|
+
{
|
|
1595
|
+
routeContext,
|
|
1596
|
+
id: relatedEntityId,
|
|
1597
|
+
},
|
|
1598
|
+
plugin,
|
|
1599
|
+
);
|
|
1600
|
+
}
|
|
1601
|
+
} else if (relationProperty.relation === "many") {
|
|
1602
|
+
if (relationProperty.linkTableName) {
|
|
1603
|
+
const targetLinks = await server.queryDatabaseObject(
|
|
1604
|
+
`SELECT ${server.queryBuilder.quoteObject(relationProperty.targetIdColumnName)} FROM ${server.queryBuilder.quoteTable({
|
|
1605
|
+
schema: relationProperty.linkSchema,
|
|
1606
|
+
tableName: relationProperty.linkTableName,
|
|
1607
|
+
})} WHERE ${server.queryBuilder.quoteObject(relationProperty.selfIdColumnName!)} = $1`,
|
|
1608
|
+
[id],
|
|
1609
|
+
routeContext?.getDbTransactionClient(),
|
|
1610
|
+
);
|
|
1611
|
+
const targetEntityIds = targetLinks.map((item) => item[relationProperty.targetIdColumnName]);
|
|
1612
|
+
|
|
1613
|
+
await server.queryDatabaseObject(
|
|
1614
|
+
`DELETE FROM ${server.queryBuilder.quoteTable({
|
|
1615
|
+
schema: relationProperty.linkSchema,
|
|
1616
|
+
tableName: relationProperty.linkTableName,
|
|
1617
|
+
})} WHERE ${server.queryBuilder.quoteObject(relationProperty.selfIdColumnName!)} = $1`,
|
|
1618
|
+
[id],
|
|
1619
|
+
routeContext?.getDbTransactionClient(),
|
|
1620
|
+
);
|
|
1621
|
+
|
|
1622
|
+
for (const targetEntityId of targetEntityIds) {
|
|
1623
|
+
await deleteEntityById(
|
|
1624
|
+
server,
|
|
1625
|
+
relationDataAccessor,
|
|
1626
|
+
{
|
|
1627
|
+
routeContext,
|
|
1628
|
+
id: targetEntityId,
|
|
1629
|
+
},
|
|
1630
|
+
plugin,
|
|
1631
|
+
);
|
|
1632
|
+
}
|
|
1633
|
+
} else {
|
|
1634
|
+
const targetModel = server.getModel({
|
|
1635
|
+
singularCode: relationProperty.targetSingularCode,
|
|
1636
|
+
});
|
|
1637
|
+
const targetRows = await server.queryDatabaseObject(
|
|
1638
|
+
`SELECT id FROM ${server.queryBuilder.quoteTable({
|
|
1639
|
+
schema: targetModel.schema,
|
|
1640
|
+
tableName: targetModel.tableName,
|
|
1641
|
+
})} WHERE ${server.queryBuilder.quoteObject(relationProperty.selfIdColumnName!)} = $1`,
|
|
1642
|
+
[id],
|
|
1643
|
+
routeContext?.getDbTransactionClient(),
|
|
1644
|
+
);
|
|
1645
|
+
const targetEntityIds = targetRows.map((item) => item.id);
|
|
1646
|
+
for (const targetEntityId of targetEntityIds) {
|
|
1647
|
+
await deleteEntityById(
|
|
1648
|
+
server,
|
|
1649
|
+
relationDataAccessor,
|
|
1650
|
+
{
|
|
1651
|
+
routeContext,
|
|
1652
|
+
id: targetEntityId,
|
|
1653
|
+
},
|
|
1654
|
+
plugin,
|
|
1655
|
+
);
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
} else if (relationProperty.entityDeletingReaction === "unlink") {
|
|
1660
|
+
if (relationProperty.relation === "one") {
|
|
1661
|
+
// do nothing, entity will be deleted later.
|
|
1662
|
+
} else if (relationProperty.relation === "many") {
|
|
1663
|
+
if (relationProperty.linkTableName) {
|
|
1664
|
+
await server.queryDatabaseObject(
|
|
1665
|
+
`DELETE FROM ${server.queryBuilder.quoteTable({
|
|
1666
|
+
schema: relationProperty.linkSchema,
|
|
1667
|
+
tableName: relationProperty.linkTableName,
|
|
1668
|
+
})}
|
|
1669
|
+
WHERE ${server.queryBuilder.quoteObject(relationProperty.selfIdColumnName!)} = $1`,
|
|
1670
|
+
[id],
|
|
1671
|
+
routeContext?.getDbTransactionClient(),
|
|
1672
|
+
);
|
|
1673
|
+
} else {
|
|
1674
|
+
const relationModel = server.getModel({
|
|
1675
|
+
singularCode: relationProperty.targetSingularCode,
|
|
1676
|
+
});
|
|
1677
|
+
await server.queryDatabaseObject(
|
|
1678
|
+
`UPDATE ${server.queryBuilder.quoteTable({
|
|
1679
|
+
schema: relationModel.schema,
|
|
1680
|
+
tableName: relationModel.tableName,
|
|
1681
|
+
})}
|
|
1682
|
+
SET ${server.queryBuilder.quoteObject(relationProperty.selfIdColumnName!)} = null
|
|
1683
|
+
WHERE ${server.queryBuilder.quoteObject(relationProperty.selfIdColumnName!)} = $1`,
|
|
1684
|
+
[id],
|
|
1685
|
+
routeContext?.getDbTransactionClient(),
|
|
1686
|
+
);
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
await dataAccessor.deleteById(id, routeContext?.getDbTransactionClient());
|
|
1693
|
+
if (model.base) {
|
|
1694
|
+
const baseDataAccessor = server.getDataAccessor({
|
|
1695
|
+
singularCode: model.base,
|
|
1696
|
+
});
|
|
1697
|
+
await baseDataAccessor.deleteById(id, routeContext?.getDbTransactionClient());
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
await server.emitEvent({
|
|
1702
|
+
eventName: "entity.delete",
|
|
1703
|
+
payload: {
|
|
1704
|
+
namespace: model.namespace,
|
|
1705
|
+
modelSingularCode: model.singularCode,
|
|
1706
|
+
before: entity,
|
|
1707
|
+
},
|
|
1708
|
+
sender: plugin,
|
|
1709
|
+
routeContext,
|
|
1710
|
+
});
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1511
1713
|
export default class EntityManager<TEntity = any> {
|
|
1512
1714
|
#server: IRpdServer;
|
|
1513
1715
|
#dataAccessor: IRpdDataAccessor;
|
|
@@ -1563,76 +1765,7 @@ export default class EntityManager<TEntity = any> {
|
|
|
1563
1765
|
}
|
|
1564
1766
|
|
|
1565
1767
|
async deleteById(options: DeleteEntityByIdOptions | string | number, plugin?: RapidPlugin): Promise<void> {
|
|
1566
|
-
|
|
1567
|
-
if (!isObject(options)) {
|
|
1568
|
-
options = {
|
|
1569
|
-
id: options,
|
|
1570
|
-
};
|
|
1571
|
-
}
|
|
1572
|
-
|
|
1573
|
-
const model = this.getModel();
|
|
1574
|
-
if (model.derivedTypePropertyCode) {
|
|
1575
|
-
throw newEntityOperationError("Delete base entity directly is not allowed.");
|
|
1576
|
-
}
|
|
1577
|
-
|
|
1578
|
-
const { id, routeContext } = options;
|
|
1579
|
-
|
|
1580
|
-
const entity = await this.findById({
|
|
1581
|
-
id,
|
|
1582
|
-
keepNonPropertyFields: true,
|
|
1583
|
-
routeContext,
|
|
1584
|
-
});
|
|
1585
|
-
|
|
1586
|
-
if (!entity) {
|
|
1587
|
-
return;
|
|
1588
|
-
}
|
|
1589
|
-
|
|
1590
|
-
await this.#server.emitEvent({
|
|
1591
|
-
eventName: "entity.beforeDelete",
|
|
1592
|
-
payload: {
|
|
1593
|
-
namespace: model.namespace,
|
|
1594
|
-
modelSingularCode: model.singularCode,
|
|
1595
|
-
before: entity,
|
|
1596
|
-
},
|
|
1597
|
-
sender: plugin,
|
|
1598
|
-
routeContext,
|
|
1599
|
-
});
|
|
1600
|
-
|
|
1601
|
-
if (model.softDelete) {
|
|
1602
|
-
let dataAccessor = model.base
|
|
1603
|
-
? this.#server.getDataAccessor({
|
|
1604
|
-
singularCode: model.base,
|
|
1605
|
-
})
|
|
1606
|
-
: this.#dataAccessor;
|
|
1607
|
-
const currentUserId = routeContext?.state?.userId;
|
|
1608
|
-
await dataAccessor.updateById(
|
|
1609
|
-
id,
|
|
1610
|
-
{
|
|
1611
|
-
deleted_at: getNowStringWithTimezone(),
|
|
1612
|
-
deleter_id: currentUserId,
|
|
1613
|
-
},
|
|
1614
|
-
routeContext?.getDbTransactionClient(),
|
|
1615
|
-
);
|
|
1616
|
-
} else {
|
|
1617
|
-
await this.#dataAccessor.deleteById(id, routeContext?.getDbTransactionClient());
|
|
1618
|
-
if (model.base) {
|
|
1619
|
-
const baseDataAccessor = this.#server.getDataAccessor({
|
|
1620
|
-
singularCode: model.base,
|
|
1621
|
-
});
|
|
1622
|
-
await baseDataAccessor.deleteById(id, routeContext?.getDbTransactionClient());
|
|
1623
|
-
}
|
|
1624
|
-
}
|
|
1625
|
-
|
|
1626
|
-
await this.#server.emitEvent({
|
|
1627
|
-
eventName: "entity.delete",
|
|
1628
|
-
payload: {
|
|
1629
|
-
namespace: model.namespace,
|
|
1630
|
-
modelSingularCode: model.singularCode,
|
|
1631
|
-
before: entity,
|
|
1632
|
-
},
|
|
1633
|
-
sender: plugin,
|
|
1634
|
-
routeContext,
|
|
1635
|
-
});
|
|
1768
|
+
return await deleteEntityById(this.#server, this.#dataAccessor, options, plugin);
|
|
1636
1769
|
}
|
|
1637
1770
|
|
|
1638
1771
|
async addRelations(options: AddEntityRelationsOptions, plugin?: RapidPlugin): Promise<void> {
|
|
@@ -1669,7 +1802,7 @@ export default class EntityManager<TEntity = any> {
|
|
|
1669
1802
|
WHERE ${queryBuilder.quoteObject(relationProperty.selfIdColumnName!)}=$1 AND ${queryBuilder.quoteObject(relationProperty.targetIdColumnName!)}=$2
|
|
1670
1803
|
)`;
|
|
1671
1804
|
const params = [id, relation.id];
|
|
1672
|
-
await server.queryDatabaseObject(command, params);
|
|
1805
|
+
await server.queryDatabaseObject(command, params, routeContext?.getDbTransactionClient());
|
|
1673
1806
|
}
|
|
1674
1807
|
}
|
|
1675
1808
|
|
|
@@ -1714,7 +1847,7 @@ export default class EntityManager<TEntity = any> {
|
|
|
1714
1847
|
const command = `DELETE FROM ${queryBuilder.quoteTable({ schema: relationProperty.linkSchema, tableName: relationProperty.linkTableName })}
|
|
1715
1848
|
WHERE ${queryBuilder.quoteObject(relationProperty.selfIdColumnName!)}=$1 AND ${queryBuilder.quoteObject(relationProperty.targetIdColumnName!)}=$2;`;
|
|
1716
1849
|
const params = [id, relation.id];
|
|
1717
|
-
await server.queryDatabaseObject(command, params);
|
|
1850
|
+
await server.queryDatabaseObject(command, params, routeContext?.getDbTransactionClient());
|
|
1718
1851
|
}
|
|
1719
1852
|
}
|
|
1720
1853
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { cloneDeep } from "lodash";
|
|
1
|
+
import { cloneDeep, filter, find } from "lodash";
|
|
2
2
|
import { IRpdServer } from "~/core/server";
|
|
3
3
|
import { RpdDataModel, RpdDataModelProperty } from "~/types";
|
|
4
4
|
|
|
@@ -14,13 +14,17 @@ export function isManyRelationProperty(property: RpdDataModelProperty) {
|
|
|
14
14
|
return isRelationProperty(property) && property.relation === "many";
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export function getEntityProperties(server: IRpdServer, model: RpdDataModel) {
|
|
18
|
-
|
|
17
|
+
export function getEntityProperties(server: IRpdServer, model: RpdDataModel, predicate?: (item: RpdDataModelProperty) => boolean) {
|
|
18
|
+
if (!predicate) {
|
|
19
|
+
return model.properties;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return filter(model.properties, predicate);
|
|
19
23
|
}
|
|
20
24
|
|
|
21
|
-
export function getEntityPropertiesIncludingBase(server: IRpdServer, model: RpdDataModel) {
|
|
25
|
+
export function getEntityPropertiesIncludingBase(server: IRpdServer, model: RpdDataModel, predicate?: (item: RpdDataModelProperty) => boolean) {
|
|
22
26
|
if (!model.base) {
|
|
23
|
-
return model
|
|
27
|
+
return getEntityProperties(server, model, predicate);
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
const baseModel = server.getModel({
|
|
@@ -28,14 +32,25 @@ export function getEntityPropertiesIncludingBase(server: IRpdServer, model: RpdD
|
|
|
28
32
|
});
|
|
29
33
|
let baseProperties: RpdDataModelProperty[] = [];
|
|
30
34
|
if (baseModel) {
|
|
31
|
-
|
|
35
|
+
if (predicate) {
|
|
36
|
+
baseProperties = filter(baseModel.properties, predicate);
|
|
37
|
+
} else {
|
|
38
|
+
baseProperties = baseModel.properties;
|
|
39
|
+
}
|
|
40
|
+
baseProperties = baseProperties.map((property) => {
|
|
32
41
|
property = cloneDeep(property);
|
|
33
42
|
property.isBaseProperty = true;
|
|
34
43
|
return property;
|
|
35
44
|
});
|
|
36
45
|
}
|
|
37
46
|
|
|
38
|
-
|
|
47
|
+
let properties: RpdDataModelProperty[];
|
|
48
|
+
if (predicate) {
|
|
49
|
+
properties = filter(model.properties, predicate);
|
|
50
|
+
} else {
|
|
51
|
+
properties = model.properties;
|
|
52
|
+
}
|
|
53
|
+
return [...baseProperties, ...properties];
|
|
39
54
|
}
|
|
40
55
|
|
|
41
56
|
export function getEntityPropertyByCode(server: IRpdServer, model: RpdDataModel, propertyCode: string): RpdDataModelProperty | undefined {
|
|
@@ -14,7 +14,16 @@ export async function handler(plugin: RapidPlugin, ctx: ActionHandlerContext, op
|
|
|
14
14
|
routeContext,
|
|
15
15
|
});
|
|
16
16
|
if (!entity) {
|
|
17
|
-
|
|
17
|
+
ctx.routerContext.response.json(
|
|
18
|
+
{
|
|
19
|
+
error: {
|
|
20
|
+
message: `${options.namespace}.${options.singularCode} with id "${id}" was not found.`,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
404,
|
|
24
|
+
);
|
|
25
|
+
// routerContext.json() function will not be called if the ctx.output is null or undefined.
|
|
26
|
+
return;
|
|
18
27
|
}
|
|
19
28
|
return entity;
|
|
20
29
|
});
|
package/src/server.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
EntityWatcherType,
|
|
18
18
|
RpdEntityCreateEventPayload,
|
|
19
19
|
EmitServerEventOptions,
|
|
20
|
+
IDatabaseClient,
|
|
20
21
|
} from "./types";
|
|
21
22
|
|
|
22
23
|
import QueryBuilder from "./queryBuilder/queryBuilder";
|
|
@@ -373,18 +374,18 @@ export class RapidServer implements IRpdServer {
|
|
|
373
374
|
return await factory.createFacility(this, options);
|
|
374
375
|
}
|
|
375
376
|
|
|
376
|
-
async queryDatabaseObject(sql: string, params?: unknown[] | Record<string, unknown
|
|
377
|
+
async queryDatabaseObject(sql: string, params?: unknown[] | Record<string, unknown>, client?: IDatabaseClient): Promise<any[]> {
|
|
377
378
|
try {
|
|
378
|
-
return await this.#databaseAccessor.queryDatabaseObject(sql, params);
|
|
379
|
+
return await this.#databaseAccessor.queryDatabaseObject(sql, params, client);
|
|
379
380
|
} catch (err) {
|
|
380
381
|
this.#logger.error("Failed to query database object.", { errorMessage: err.message, sql, params });
|
|
381
382
|
throw err;
|
|
382
383
|
}
|
|
383
384
|
}
|
|
384
385
|
|
|
385
|
-
async tryQueryDatabaseObject(sql: string, params?: unknown[] | Record<string, unknown
|
|
386
|
+
async tryQueryDatabaseObject(sql: string, params?: unknown[] | Record<string, unknown>, client?: IDatabaseClient): Promise<any[]> {
|
|
386
387
|
try {
|
|
387
|
-
return await this.queryDatabaseObject(sql, params);
|
|
388
|
+
return await this.queryDatabaseObject(sql, params, client);
|
|
388
389
|
} catch (err) {
|
|
389
390
|
this.#logger.error("Failed to query database object.", { errorMessage: err.message, sql, params });
|
|
390
391
|
}
|
|
@@ -400,13 +401,14 @@ export class RapidServer implements IRpdServer {
|
|
|
400
401
|
const rapidRequest = new RapidRequest(this, request);
|
|
401
402
|
await rapidRequest.parseBody();
|
|
402
403
|
const routeContext: RouteContext = new RouteContext(this, rapidRequest);
|
|
404
|
+
const { response } = routeContext;
|
|
403
405
|
|
|
404
406
|
try {
|
|
405
407
|
await this.#pluginManager.onPrepareRouteContext(routeContext);
|
|
406
408
|
await this.#buildedRoutes(routeContext, next);
|
|
407
409
|
} catch (ex) {
|
|
408
410
|
this.#logger.error("handle request error:", ex);
|
|
409
|
-
|
|
411
|
+
response.json(
|
|
410
412
|
{
|
|
411
413
|
error: {
|
|
412
414
|
message: ex.message || ex,
|
|
@@ -415,7 +417,18 @@ export class RapidServer implements IRpdServer {
|
|
|
415
417
|
500,
|
|
416
418
|
);
|
|
417
419
|
}
|
|
418
|
-
|
|
420
|
+
|
|
421
|
+
if (!response.status && !response.body) {
|
|
422
|
+
response.json(
|
|
423
|
+
{
|
|
424
|
+
error: {
|
|
425
|
+
message: "No route handler was found to handle this request.",
|
|
426
|
+
},
|
|
427
|
+
},
|
|
428
|
+
404,
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
return response.getResponse();
|
|
419
432
|
}
|
|
420
433
|
|
|
421
434
|
async beforeRunRouteActions(handlerContext: ActionHandlerContext) {
|
package/src/types.ts
CHANGED
|
@@ -314,6 +314,11 @@ export interface RpdDataModelProperty {
|
|
|
314
314
|
*/
|
|
315
315
|
linkSchema?: string;
|
|
316
316
|
|
|
317
|
+
/**
|
|
318
|
+
* 当删除实体时,针对关系属性的联动处理。
|
|
319
|
+
*/
|
|
320
|
+
entityDeletingReaction?: RpdEntityDeleteRelationPropertyReaction;
|
|
321
|
+
|
|
317
322
|
/**
|
|
318
323
|
* 当设置为`true`时,仅允许在创建时设置此属性的值,不允许更新。
|
|
319
324
|
*/
|
|
@@ -341,6 +346,8 @@ export type RpdDataPropertyTypes =
|
|
|
341
346
|
| "image"
|
|
342
347
|
| "image[]";
|
|
343
348
|
|
|
349
|
+
export type RpdEntityDeleteRelationPropertyReaction = "doNothing" | "unlink" | "cascadingDelete";
|
|
350
|
+
|
|
344
351
|
/**
|
|
345
352
|
* 数据字典
|
|
346
353
|
*/
|