Skip to content

Commit 8ca1841

Browse files
authored
Merge pull request #1775 from rocket-admin/backend_unchashed_table_endpoints
feat: add uncached option to data structures and update use cases for cache invalidation
2 parents 8627817 + 8a24296 commit 8ca1841

7 files changed

Lines changed: 84 additions & 8 deletions

File tree

backend/src/entities/table/application/data-structures/delete-row-from-table.ds.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export class DeleteRowFromTableDs {
44
primaryKey: Record<string, unknown>;
55
tableName: string;
66
userId: string;
7+
uncached?: boolean;
78
}
89

910
export class DeleteRowsFromTableDs {

backend/src/entities/table/application/data-structures/get-row-by-primary-key.ds.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ export class GetRowByPrimaryKeyDs {
44
primaryKey: Record<string, unknown>;
55
tableName: string;
66
userId: string;
7+
uncached?: boolean;
78
}

backend/src/entities/table/application/data-structures/update-row-in-table.ds.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ import { AddRowInTableDs } from './add-row-in-table.ds.js';
22

33
export class UpdateRowInTableDs extends AddRowInTableDs {
44
primaryKey: Record<string, unknown>;
5+
uncached?: boolean;
56
}

backend/src/entities/table/table.controller.ts

Lines changed: 69 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -261,10 +261,11 @@ export class TableController {
261261
@ApiQuery({ name: 'perPage', required: false })
262262
@ApiQuery({ name: 'search', required: false })
263263
@ApiQuery({
264-
name: 'uncached',
264+
name: '_uncached',
265265
required: false,
266266
type: Boolean,
267-
description: 'Invalidate table metadata cache before reading rows',
267+
description:
268+
'Invalidate table metadata cache before reading rows. Underscore prefix avoids column-name collisions.',
268269
})
269270
@UseGuards(TableReadGuard)
270271
@Timeout(TimeoutDefaults.EXTENDED)
@@ -276,7 +277,7 @@ export class TableController {
276277
@Query('page') page: string,
277278
@Query('perPage') perPage: string,
278279
@Query('search') searchingFieldValue: string,
279-
@Query('uncached') uncached: string,
280+
@Query('_uncached') uncachedFlag: string,
280281
@Query() query: Record<string, string>,
281282
@SlugUuid('connectionId') connectionId: string,
282283
@UserId() userId: string,
@@ -310,12 +311,12 @@ export class TableController {
310311
masterPwd: masterPwd,
311312
page: parsedPage,
312313
perPage: parsedPerPage,
313-
query: query,
314+
query: this.stripReservedQueryParams(query),
314315
searchingFieldValue: searchingFieldValue,
315316
tableName: tableName,
316317
userId: userId,
317318
filters: body?.filters,
318-
uncached: uncached === 'true',
319+
uncached: uncachedFlag === 'true',
319320
};
320321
return await this.getTableRowsUseCase.execute(inputData, InTransactionEnum.OFF);
321322
}
@@ -439,11 +440,18 @@ export class TableController {
439440
type: TableRowRODs,
440441
})
441442
@ApiQuery({ name: 'tableName', required: true })
443+
@ApiQuery({
444+
name: '_uncached',
445+
required: false,
446+
type: Boolean,
447+
description: 'Invalidate table metadata cache before reading. Underscore prefix avoids column-name collisions.',
448+
})
442449
@UseGuards(TableEditGuard)
443450
@Put('/table/row/:connectionId')
444451
async updateRowInTable(
445452
@Body() body: Record<string, unknown>,
446453
@Query() query: Record<string, string>,
454+
@Query('_uncached') uncachedFlag: string,
447455
@UserId() userId: string,
448456
@MasterPassword() masterPwd: string,
449457
@SlugUuid('connectionId') connectionId: string,
@@ -457,7 +465,16 @@ export class TableController {
457465
HttpStatus.BAD_REQUEST,
458466
);
459467
}
460-
const primaryKeys = await this.getPrimaryKeys(userId, connectionId, tableName, query, masterPwd);
468+
const uncached = uncachedFlag === 'true';
469+
const primaryKeyQuery = this.stripReservedQueryParams(query);
470+
const primaryKeys = await this.getPrimaryKeys(
471+
userId,
472+
connectionId,
473+
tableName,
474+
primaryKeyQuery,
475+
masterPwd,
476+
uncached,
477+
);
461478
const propertiesArray = primaryKeys.map((el) => {
462479
return Object.entries(el)[0];
463480
});
@@ -470,6 +487,7 @@ export class TableController {
470487
row: body,
471488
tableName: tableName,
472489
userId: userId,
490+
uncached: uncached,
473491
};
474492
return await this.updateRowInTableUseCase.execute(inputData, InTransactionEnum.OFF);
475493
}
@@ -484,16 +502,32 @@ export class TableController {
484502
type: DeletedRowFromTableDs,
485503
})
486504
@ApiQuery({ name: 'tableName', required: true })
505+
@ApiQuery({
506+
name: '_uncached',
507+
required: false,
508+
type: Boolean,
509+
description: 'Invalidate table metadata cache before reading. Underscore prefix avoids column-name collisions.',
510+
})
487511
@UseGuards(TableDeleteGuard)
488512
@Delete('/table/row/:connectionId')
489513
async deleteRowInTable(
490514
@Query() query: Record<string, string>,
515+
@Query('_uncached') uncachedFlag: string,
491516
@MasterPassword() masterPwd: string,
492517
@SlugUuid('connectionId') connectionId: string,
493518
@UserId() userId: string,
494519
@QueryTableName() tableName: string,
495520
): Promise<DeletedRowFromTableDs> {
496-
const primaryKeys = await this.getPrimaryKeys(userId, connectionId, tableName, query, masterPwd);
521+
const uncached = uncachedFlag === 'true';
522+
const primaryKeyQuery = this.stripReservedQueryParams(query);
523+
const primaryKeys = await this.getPrimaryKeys(
524+
userId,
525+
connectionId,
526+
tableName,
527+
primaryKeyQuery,
528+
masterPwd,
529+
uncached,
530+
);
497531
const propertiesArray = primaryKeys.map((el) => {
498532
return Object.entries(el)[0];
499533
});
@@ -513,6 +547,7 @@ export class TableController {
513547
primaryKey: primaryKey,
514548
tableName: tableName,
515549
userId: userId,
550+
uncached: uncached,
516551
};
517552
return await this.deleteRowFromTableUseCase.execute(inputData, InTransactionEnum.OFF);
518553
}
@@ -614,16 +649,32 @@ export class TableController {
614649
type: TableRowRODs,
615650
})
616651
@ApiQuery({ name: 'tableName', required: true })
652+
@ApiQuery({
653+
name: '_uncached',
654+
required: false,
655+
type: Boolean,
656+
description: 'Invalidate table metadata cache before reading. Underscore prefix avoids column-name collisions.',
657+
})
617658
@UseGuards(TableReadGuard)
618659
@Get('/table/row/:connectionId')
619660
async getRowByPrimaryKey(
620661
@Query() query: Record<string, string>,
662+
@Query('_uncached') uncachedFlag: string,
621663
@MasterPassword() masterPwd: string,
622664
@SlugUuid('connectionId') connectionId: string,
623665
@UserId() userId: string,
624666
@QueryTableName() tableName: string,
625667
): Promise<TableRowRODs> {
626-
const primaryKeys = await this.getPrimaryKeys(userId, connectionId, tableName, query, masterPwd);
668+
const uncached = uncachedFlag === 'true';
669+
const primaryKeyQuery = this.stripReservedQueryParams(query);
670+
const primaryKeys = await this.getPrimaryKeys(
671+
userId,
672+
connectionId,
673+
tableName,
674+
primaryKeyQuery,
675+
masterPwd,
676+
uncached,
677+
);
627678

628679
const propertiesArray = primaryKeys.map((el) => {
629680
return Object.entries(el)[0];
@@ -644,6 +695,7 @@ export class TableController {
644695
primaryKey: primaryKey,
645696
tableName: tableName,
646697
userId: userId,
698+
uncached: uncached,
647699
};
648700
return await this.getRowByPrimaryKeyUseCase.execute(inputData, InTransactionEnum.OFF);
649701
} finally {
@@ -767,12 +819,18 @@ export class TableController {
767819
};
768820
}
769821

822+
private stripReservedQueryParams(query: Record<string, string>): Record<string, string> {
823+
const { _uncached: _uncachedReserved, ...rest } = query ?? {};
824+
return rest;
825+
}
826+
770827
private async getPrimaryKeys(
771828
userId: string,
772829
connectionId: string,
773830
tableName: string,
774831
query: Record<string, string>,
775832
masterPwd: string,
833+
uncached = false,
776834
): Promise<Array<Record<string, unknown>>> {
777835
const primaryKeys = [];
778836
const connection = await this._dbContext.connectionRepository.findAndDecryptConnection(connectionId, masterPwd);
@@ -781,6 +839,9 @@ export class TableController {
781839
userEmail = await this._dbContext.userRepository.getUserEmailOrReturnNull(userId);
782840
}
783841
const dao = getDataAccessObject(connection);
842+
if (uncached) {
843+
dao.invalidateMetadataCache();
844+
}
784845

785846
const tablesInConnection = await dao.getTablesFromDB(userEmail);
786847
const tableNames = tablesInConnection.map((table) => table.tableName);

backend/src/entities/table/use-cases/delete-row-from-table.use.case.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export class DeleteRowFromTableUseCase
4141
protected async implementation(inputData: DeleteRowFromTableDs): Promise<DeletedRowFromTableDs> {
4242
// eslint-disable-next-line prefer-const
4343
let { connectionId, masterPwd, primaryKey, tableName, userId } = inputData;
44+
const { uncached } = inputData;
4445

4546
let operationResult = OperationResultStatusEnum.unknown;
4647
if (!primaryKey) {
@@ -56,6 +57,9 @@ export class DeleteRowFromTableUseCase
5657
validateConnection(connection);
5758

5859
const dao = getDataAccessObject(connection);
60+
if (uncached) {
61+
dao.invalidateMetadataCache();
62+
}
5963
const userEmail = await getUserEmailForAgent(connection, userId, this._dbContext.userRepository);
6064

6165
const isView = await dao.isView(tableName, userEmail);

backend/src/entities/table/use-cases/get-row-by-primary-key.use.case.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export class GetRowByPrimaryKeyUseCase
4545

4646
protected async implementation(inputData: GetRowByPrimaryKeyDs): Promise<TableRowRODs> {
4747
let { connectionId, masterPwd, primaryKey, tableName, userId } = inputData;
48+
const { uncached } = inputData;
4849
if (!primaryKey) {
4950
throw new HttpException(
5051
{
@@ -62,6 +63,9 @@ export class GetRowByPrimaryKeyUseCase
6263
const userEmail = await getUserEmailForAgent(connection, userId, this._dbContext.userRepository);
6364

6465
await validateSchemaCache(dao, userEmail);
66+
if (uncached) {
67+
dao.invalidateMetadataCache();
68+
}
6569

6670
let [
6771
tableStructure,

backend/src/entities/table/use-cases/update-row-in-table.use.case.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export class UpdateRowInTableUseCase
5757

5858
protected async implementation(inputData: UpdateRowInTableDs): Promise<TableRowRODs> {
5959
let { connectionId, masterPwd, primaryKey, row, tableName, userId } = inputData;
60+
const { uncached } = inputData;
6061
let operationResult = OperationResultStatusEnum.unknown;
6162

6263
const errors = [];
@@ -68,6 +69,9 @@ export class UpdateRowInTableUseCase
6869
validateConnection(connection);
6970

7071
const dao = getDataAccessObject(connection);
72+
if (uncached) {
73+
dao.invalidateMetadataCache();
74+
}
7175

7276
const userEmail = await getUserEmailForAgent(connection, userId, this._dbContext.userRepository);
7377
const isView = await dao.isView(tableName, userEmail);

0 commit comments

Comments
 (0)