Skip to content

Commit 66b0377

Browse files
committed
feat: implement cascading deletion utility for child resources
1 parent ca5bf91 commit 66b0377

File tree

3 files changed

+46
-46
lines changed

3 files changed

+46
-46
lines changed

adminforth/modules/configValidator.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
import AdminForth from "adminforth";
3131
import { AdminForthConfigMenuItem } from "adminforth";
3232
import { afLogger } from "./logger.js";
33+
import {cascadeChildrenDelete} from './utils.js'
3334
import AdminForthRestAPI from './restApi.js';
3435

3536
export default class ConfigValidator implements IConfigValidator {
@@ -282,9 +283,9 @@ export default class ConfigValidator implements IConfigValidator {
282283
return;
283284
}
284285

285-
const restApi = new AdminForthRestAPI (this.adminforth)
286-
await restApi.deleteWithCascade(res as AdminForthResource, recordId, { adminUser, response});
287-
286+
await connector.deleteRecord({ resource: res as AdminForthResource, recordId });
287+
await cascadeChildrenDelete(res as AdminForthResource, recordId, { adminUser, response}, this.adminforth);
288+
288289
await Promise.all(
289290
(res.hooks.delete.afterSave).map(
290291
async (hook) => {

adminforth/modules/restApi.ts

Lines changed: 3 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import {
1515
Filters,
1616
} from "../types/Back.js";
1717

18+
import {cascadeChildrenDelete} from './utils.js'
19+
1820
import { afLogger } from "./logger.js";
1921

2022
import { ADMINFORTH_VERSION, listify, md5hash, getLoginPromptHTML } from './utils.js';
@@ -152,47 +154,6 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
152154
}
153155
}
154156
}
155-
async deleteWithCascade(resource: AdminForthResource, primaryKey: string, context: {adminUser: any, response: any}): Promise<{ error: string | null }> {
156-
const { adminUser, response } = context;
157-
158-
const record = await this.adminforth.connectors[resource.dataSource].getRecordByPrimaryKey(resource, primaryKey);
159-
160-
if (!record){
161-
return {error: `Record with id ${primaryKey} not found`};
162-
}
163-
164-
const childResources = this.adminforth.config.resources.filter(r =>r.columns.some(c => c.foreignResource?.resourceId === resource.resourceId));
165-
166-
for (const childRes of childResources) {
167-
const foreignColumn = childRes.columns.find(c => c.foreignResource?.resourceId === resource.resourceId);
168-
169-
if (!foreignColumn?.foreignResource?.onDelete) continue;
170-
171-
const strategy = foreignColumn.foreignResource.onDelete;
172-
173-
const childRecords = await this.adminforth.resource(childRes.resourceId).list(Filters.EQ(foreignColumn.name, primaryKey));
174-
175-
const childPk = childRes.columns.find(c => c.primaryKey)?.name;
176-
if (!childPk) continue;
177-
178-
if (strategy === 'cascade') {
179-
for (const childRecord of childRecords) {
180-
const childResult = await this.deleteWithCascade(childRes, childRecord[childPk], context);
181-
if (childResult?.error) {
182-
return childResult;
183-
}
184-
}
185-
}
186-
187-
if (strategy === 'setNull') {
188-
for (const childRecord of childRecords) {
189-
await this.adminforth.resource(childRes.resourceId).update(childRecord[childPk], {[foreignColumn.name]: null});
190-
}
191-
}
192-
}
193-
const deleteResult = await this.adminforth.deleteResourceRecord({resource, record, adminUser, recordId: primaryKey, response});
194-
return { error: deleteResult.error};
195-
}
196157

197158
registerEndpoints(server: IHttpServer) {
198159
server.endpoint({
@@ -1522,7 +1483,7 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI {
15221483
return { error };
15231484
}
15241485

1525-
const { error: cascadeError } = await this.deleteWithCascade(resource, body.primaryKey, {adminUser, response});
1486+
const { error: cascadeError } = await cascadeChildrenDelete(resource, body.primaryKey, {adminUser, response}, this.adminforth);
15261487
if (cascadeError) {
15271488
return { error: cascadeError };
15281489
}

adminforth/modules/utils.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { fileURLToPath } from 'url';
33
import fs from 'fs';
44
import Fuse from 'fuse.js';
55
import crypto from 'crypto';
6-
import AdminForth, { AdminForthConfig, AdminForthResourceColumnInputCommon, Predicate } from '../index.js';
6+
import { AdminForthConfig, AdminForthResource, AdminForthResourceColumnInputCommon,Filters, IAdminForth, Predicate } from '../index.js';
77
import { RateLimiterMemory, RateLimiterAbstract } from "rate-limiter-flexible";
88
// @ts-ignore-next-line
99

@@ -479,3 +479,41 @@ export function slugifyString(str: string): string {
479479
.replace(/\s+/g, '-')
480480
.replace(/[^a-z0-9-_]/g, '-');
481481
}
482+
483+
export async function cascadeChildrenDelete(resource: AdminForthResource, primaryKey: string, context: {adminUser: any, response: any}, adminforth: IAdminForth): Promise<{ error: string | null }> {
484+
const { adminUser, response } = context;
485+
486+
const childResources = adminforth.config.resources.filter(r =>r.columns.some(c => c.foreignResource?.resourceId === resource.resourceId));
487+
488+
for (const childRes of childResources) {
489+
const foreignColumn = childRes.columns.find(c => c.foreignResource?.resourceId === resource.resourceId);
490+
491+
if (!foreignColumn?.foreignResource?.onDelete) continue;
492+
493+
const strategy = foreignColumn.foreignResource.onDelete;
494+
495+
const childRecords = await adminforth.resource(childRes.resourceId).list(Filters.EQ(foreignColumn.name, primaryKey));
496+
497+
const childPk = childRes.columns.find(c => c.primaryKey)?.name;
498+
if (!childPk) continue;
499+
500+
if (strategy === 'cascade') {
501+
for (const childRecord of childRecords) {
502+
const childResult = await cascadeChildrenDelete(childRes, childRecord[childPk], context, adminforth);
503+
const deleteChild = await adminforth.deleteResourceRecord({resource: childRes, record: childRecord, adminUser, recordId: childRecord[childPk], response});
504+
if (deleteChild.error) return { error: deleteChild.error };
505+
if (childResult?.error) {
506+
return childResult;
507+
}
508+
}
509+
}
510+
511+
if (strategy === 'setNull') {
512+
for (const childRecord of childRecords) {
513+
await adminforth.resource(childRes.resourceId).update(childRecord[childPk], {[foreignColumn.name]: null});
514+
}
515+
}
516+
}
517+
518+
return { error: null };
519+
}

0 commit comments

Comments
 (0)