Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export class DeleteRowFromTableDs {
primaryKey: Record<string, unknown>;
tableName: string;
userId: string;
uncached?: boolean;
}

export class DeleteRowsFromTableDs {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export class GetRowByPrimaryKeyDs {
primaryKey: Record<string, unknown>;
tableName: string;
userId: string;
uncached?: boolean;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ import { AddRowInTableDs } from './add-row-in-table.ds.js';

export class UpdateRowInTableDs extends AddRowInTableDs {
primaryKey: Record<string, unknown>;
uncached?: boolean;
}
77 changes: 69 additions & 8 deletions backend/src/entities/table/table.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,10 +261,11 @@ export class TableController {
@ApiQuery({ name: 'perPage', required: false })
@ApiQuery({ name: 'search', required: false })
@ApiQuery({
name: 'uncached',
name: '_uncached',
required: false,
type: Boolean,
description: 'Invalidate table metadata cache before reading rows',
description:
'Invalidate table metadata cache before reading rows. Underscore prefix avoids column-name collisions.',
})
@UseGuards(TableReadGuard)
@Timeout(TimeoutDefaults.EXTENDED)
Expand All @@ -276,7 +277,7 @@ export class TableController {
@Query('page') page: string,
@Query('perPage') perPage: string,
@Query('search') searchingFieldValue: string,
@Query('uncached') uncached: string,
@Query('_uncached') uncachedFlag: string,
@Query() query: Record<string, string>,
@SlugUuid('connectionId') connectionId: string,
@UserId() userId: string,
Expand Down Expand Up @@ -310,12 +311,12 @@ export class TableController {
masterPwd: masterPwd,
page: parsedPage,
perPage: parsedPerPage,
query: query,
query: this.stripReservedQueryParams(query),
searchingFieldValue: searchingFieldValue,
tableName: tableName,
userId: userId,
filters: body?.filters,
uncached: uncached === 'true',
uncached: uncachedFlag === 'true',
};
return await this.getTableRowsUseCase.execute(inputData, InTransactionEnum.OFF);
}
Expand Down Expand Up @@ -439,11 +440,18 @@ export class TableController {
type: TableRowRODs,
})
@ApiQuery({ name: 'tableName', required: true })
@ApiQuery({
name: '_uncached',
required: false,
type: Boolean,
description: 'Invalidate table metadata cache before reading. Underscore prefix avoids column-name collisions.',
})
@UseGuards(TableEditGuard)
@Put('/table/row/:connectionId')
async updateRowInTable(
@Body() body: Record<string, unknown>,
@Query() query: Record<string, string>,
@Query('_uncached') uncachedFlag: string,
@UserId() userId: string,
@MasterPassword() masterPwd: string,
@SlugUuid('connectionId') connectionId: string,
Expand All @@ -457,7 +465,16 @@ export class TableController {
HttpStatus.BAD_REQUEST,
);
}
const primaryKeys = await this.getPrimaryKeys(userId, connectionId, tableName, query, masterPwd);
const uncached = uncachedFlag === 'true';
const primaryKeyQuery = this.stripReservedQueryParams(query);
const primaryKeys = await this.getPrimaryKeys(
userId,
connectionId,
tableName,
primaryKeyQuery,
masterPwd,
uncached,
);
const propertiesArray = primaryKeys.map((el) => {
return Object.entries(el)[0];
});
Expand All @@ -470,6 +487,7 @@ export class TableController {
row: body,
tableName: tableName,
userId: userId,
uncached: uncached,
};
return await this.updateRowInTableUseCase.execute(inputData, InTransactionEnum.OFF);
}
Expand All @@ -484,16 +502,32 @@ export class TableController {
type: DeletedRowFromTableDs,
})
@ApiQuery({ name: 'tableName', required: true })
@ApiQuery({
name: '_uncached',
required: false,
type: Boolean,
description: 'Invalidate table metadata cache before reading. Underscore prefix avoids column-name collisions.',
})
@UseGuards(TableDeleteGuard)
@Delete('/table/row/:connectionId')
async deleteRowInTable(
@Query() query: Record<string, string>,
@Query('_uncached') uncachedFlag: string,
@MasterPassword() masterPwd: string,
@SlugUuid('connectionId') connectionId: string,
@UserId() userId: string,
@QueryTableName() tableName: string,
): Promise<DeletedRowFromTableDs> {
const primaryKeys = await this.getPrimaryKeys(userId, connectionId, tableName, query, masterPwd);
const uncached = uncachedFlag === 'true';
const primaryKeyQuery = this.stripReservedQueryParams(query);
const primaryKeys = await this.getPrimaryKeys(
userId,
connectionId,
tableName,
primaryKeyQuery,
masterPwd,
uncached,
);
const propertiesArray = primaryKeys.map((el) => {
return Object.entries(el)[0];
});
Expand All @@ -513,6 +547,7 @@ export class TableController {
primaryKey: primaryKey,
tableName: tableName,
userId: userId,
uncached: uncached,
};
return await this.deleteRowFromTableUseCase.execute(inputData, InTransactionEnum.OFF);
}
Expand Down Expand Up @@ -614,16 +649,32 @@ export class TableController {
type: TableRowRODs,
})
@ApiQuery({ name: 'tableName', required: true })
@ApiQuery({
name: '_uncached',
required: false,
type: Boolean,
description: 'Invalidate table metadata cache before reading. Underscore prefix avoids column-name collisions.',
})
@UseGuards(TableReadGuard)
@Get('/table/row/:connectionId')
async getRowByPrimaryKey(
@Query() query: Record<string, string>,
@Query('_uncached') uncachedFlag: string,
@MasterPassword() masterPwd: string,
@SlugUuid('connectionId') connectionId: string,
@UserId() userId: string,
@QueryTableName() tableName: string,
): Promise<TableRowRODs> {
const primaryKeys = await this.getPrimaryKeys(userId, connectionId, tableName, query, masterPwd);
const uncached = uncachedFlag === 'true';
const primaryKeyQuery = this.stripReservedQueryParams(query);
const primaryKeys = await this.getPrimaryKeys(
userId,
connectionId,
tableName,
primaryKeyQuery,
masterPwd,
uncached,
);

const propertiesArray = primaryKeys.map((el) => {
return Object.entries(el)[0];
Expand All @@ -644,6 +695,7 @@ export class TableController {
primaryKey: primaryKey,
tableName: tableName,
userId: userId,
uncached: uncached,
};
return await this.getRowByPrimaryKeyUseCase.execute(inputData, InTransactionEnum.OFF);
} finally {
Expand Down Expand Up @@ -767,12 +819,18 @@ export class TableController {
};
}

private stripReservedQueryParams(query: Record<string, string>): Record<string, string> {
const { _uncached: _uncachedReserved, ...rest } = query ?? {};
return rest;
}

private async getPrimaryKeys(
userId: string,
connectionId: string,
tableName: string,
query: Record<string, string>,
masterPwd: string,
uncached = false,
): Promise<Array<Record<string, unknown>>> {
const primaryKeys = [];
const connection = await this._dbContext.connectionRepository.findAndDecryptConnection(connectionId, masterPwd);
Expand All @@ -781,6 +839,9 @@ export class TableController {
userEmail = await this._dbContext.userRepository.getUserEmailOrReturnNull(userId);
}
const dao = getDataAccessObject(connection);
if (uncached) {
dao.invalidateMetadataCache();
}

const tablesInConnection = await dao.getTablesFromDB(userEmail);
const tableNames = tablesInConnection.map((table) => table.tableName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export class DeleteRowFromTableUseCase
protected async implementation(inputData: DeleteRowFromTableDs): Promise<DeletedRowFromTableDs> {
// eslint-disable-next-line prefer-const
let { connectionId, masterPwd, primaryKey, tableName, userId } = inputData;
const { uncached } = inputData;

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

const dao = getDataAccessObject(connection);
if (uncached) {
dao.invalidateMetadataCache();
}
const userEmail = await getUserEmailForAgent(connection, userId, this._dbContext.userRepository);

const isView = await dao.isView(tableName, userEmail);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export class GetRowByPrimaryKeyUseCase

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

await validateSchemaCache(dao, userEmail);
if (uncached) {
dao.invalidateMetadataCache();
}

let [
tableStructure,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export class UpdateRowInTableUseCase

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

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

const dao = getDataAccessObject(connection);
if (uncached) {
dao.invalidateMetadataCache();
}

const userEmail = await getUserEmailForAgent(connection, userId, this._dbContext.userRepository);
const isView = await dao.isView(tableName, userEmail);
Expand Down
Loading