diff --git a/backend/src/entities/table/application/data-structures/found-table-rows.ds.ts b/backend/src/entities/table/application/data-structures/found-table-rows.ds.ts index 317f7ba4b..c3200f5fa 100644 --- a/backend/src/entities/table/application/data-structures/found-table-rows.ds.ts +++ b/backend/src/entities/table/application/data-structures/found-table-rows.ds.ts @@ -10,124 +10,127 @@ import { FoundActionEventDTO } from '../../../table-actions/table-action-rules-m import { CreatedTableFilterRO } from '../../../table-filters/application/response-objects/created-table-filters.ro.js'; export class TableSettingsInRowsDS { - @ApiProperty({ isArray: true }) - sortable_by: Array; + @ApiProperty({ isArray: true }) + sortable_by: Array; - @ApiProperty({ enum: QueryOrderingEnum }) - ordering: QueryOrderingEnum; + @ApiProperty({ enum: QueryOrderingEnum }) + ordering: QueryOrderingEnum; - @ApiProperty() - identity_column: string; + @ApiProperty() + ordering_field: string; - @ApiProperty({ isArray: true }) - list_fields: Array; + @ApiProperty() + identity_column: string; - @ApiProperty() - allow_csv_export: boolean; + @ApiProperty({ isArray: true }) + list_fields: Array; - @ApiProperty() - allow_csv_import: boolean; + @ApiProperty() + allow_csv_export: boolean; - @ApiProperty() - can_delete: boolean; + @ApiProperty() + allow_csv_import: boolean; - @ApiProperty() - can_update: boolean; + @ApiProperty() + can_delete: boolean; - @ApiProperty() - can_add: boolean; + @ApiProperty() + can_update: boolean; + + @ApiProperty() + can_add: boolean; } export class FoundTableRowsDs { - @ApiProperty({ isArray: true }) - rows: Array>; + @ApiProperty({ isArray: true }) + rows: Array>; - @ApiProperty({ isArray: true, type: PrimaryKeyDS }) - primaryColumns: Array; + @ApiProperty({ isArray: true, type: PrimaryKeyDS }) + primaryColumns: Array; - @ApiProperty() - pagination: RowsPaginationDS; + @ApiProperty() + pagination: RowsPaginationDS; - @ApiProperty({ isArray: true }) - sortable_by: Array; + @ApiProperty({ isArray: true }) + sortable_by: Array; - @ApiProperty() - ordering_field: string; + @ApiProperty() + ordering_field: string; - @ApiProperty({ enum: QueryOrderingEnum }) - ordering: QueryOrderingEnum; + @ApiProperty({ enum: QueryOrderingEnum }) + ordering: QueryOrderingEnum; - @ApiProperty({ isArray: true }) - columns_view: Array; + @ApiProperty({ isArray: true }) + columns_view: Array; - @ApiProperty({ isArray: true, type: FullTableStructureDs }) - structure: Array; + @ApiProperty({ isArray: true, type: FullTableStructureDs }) + structure: Array; - @ApiProperty({ isArray: true, type: ForeignKeyDS }) - foreignKeys: Array; + @ApiProperty({ isArray: true, type: ForeignKeyDS }) + foreignKeys: Array; - @ApiProperty() - configured: boolean; + @ApiProperty() + configured: boolean; - @ApiProperty({ isArray: true }) - widgets: Array; + @ApiProperty({ isArray: true }) + widgets: Array; - @ApiProperty() - identity_column: string; + @ApiProperty() + identity_column: string; - @ApiProperty() - table_permissions: TablePermissionDs; + @ApiProperty() + table_permissions: TablePermissionDs; - @ApiProperty({ isArray: true, type: FoundActionEventDTO }) - action_events: Array; + @ApiProperty({ isArray: true, type: FoundActionEventDTO }) + action_events: Array; - @ApiProperty() - large_dataset: boolean; + @ApiProperty() + large_dataset: boolean; - @ApiProperty() - allow_csv_export: boolean; + @ApiProperty() + allow_csv_export: boolean; - @ApiProperty() - allow_csv_import: boolean; + @ApiProperty() + allow_csv_import: boolean; - @ApiProperty() - can_delete: boolean; + @ApiProperty() + can_delete: boolean; - @ApiProperty() - can_update: boolean; + @ApiProperty() + can_update: boolean; - @ApiProperty() - can_add: boolean; + @ApiProperty() + can_add: boolean; - @ApiProperty({ type: CreatedTableFilterRO, isArray: true }) - saved_filters: Array; + @ApiProperty({ type: CreatedTableFilterRO, isArray: true }) + saved_filters: Array; - @ApiProperty({ type: TableSettingsInRowsDS }) - table_settings: TableSettingsInRowsDS; + @ApiProperty({ type: TableSettingsInRowsDS }) + table_settings: TableSettingsInRowsDS; } export class OrderingFiledDs { - @ApiProperty() - field: string; + @ApiProperty() + field: string; - @ApiProperty({ enum: QueryOrderingEnum }) - value: QueryOrderingEnum; + @ApiProperty({ enum: QueryOrderingEnum }) + value: QueryOrderingEnum; } export class FilteringFieldsDs { - @ApiProperty({ enum: FilterCriteriaEnum }) - criteria: FilterCriteriaEnum; + @ApiProperty({ enum: FilterCriteriaEnum }) + criteria: FilterCriteriaEnum; - @ApiProperty() - field: string; + @ApiProperty() + field: string; - @ApiProperty() - value: string; + @ApiProperty() + value: string; } export class AutocompleteFieldsDs { - @ApiProperty({ isArray: true }) - fields: Array; + @ApiProperty({ isArray: true }) + fields: Array; - @ApiProperty() - value: string; + @ApiProperty() + value: string; } diff --git a/backend/src/entities/table/use-cases/add-row-in-table.use.case.ts b/backend/src/entities/table/use-cases/add-row-in-table.use.case.ts index aa234953b..7a8407531 100644 --- a/backend/src/entities/table/use-cases/add-row-in-table.use.case.ts +++ b/backend/src/entities/table/use-cases/add-row-in-table.use.case.ts @@ -9,10 +9,10 @@ import AbstractUseCase from '../../../common/abstract-use.case.js'; import { IGlobalDatabaseContext } from '../../../common/application/global-database-context.interface.js'; import { BaseType } from '../../../common/data-injection.tokens.js'; import { - AmplitudeEventTypeEnum, - LogOperationTypeEnum, - OperationResultStatusEnum, - WidgetTypeEnum, + AmplitudeEventTypeEnum, + LogOperationTypeEnum, + OperationResultStatusEnum, + WidgetTypeEnum, } from '../../../enums/index.js'; import { TableActionEventEnum } from '../../../enums/table-action-event-enum.js'; import { NonAvailableInFreePlanException } from '../../../exceptions/custom-exceptions/non-available-in-free-plan-exception.js'; @@ -35,288 +35,289 @@ import { IAddRowInTable } from './table-use-cases.interface.js'; @Injectable() export class AddRowInTableUseCase extends AbstractUseCase implements IAddRowInTable { - constructor( - @Inject(BaseType.GLOBAL_DB_CONTEXT) - protected _dbContext: IGlobalDatabaseContext, - private amplitudeService: AmplitudeService, - private tableLogsService: TableLogsService, - private tableActionActivationService: TableActionActivationService, - ) { - super(); - } + constructor( + @Inject(BaseType.GLOBAL_DB_CONTEXT) + protected _dbContext: IGlobalDatabaseContext, + private amplitudeService: AmplitudeService, + private tableLogsService: TableLogsService, + private tableActionActivationService: TableActionActivationService, + ) { + super(); + } - protected async implementation(inputData: AddRowInTableDs): Promise { - const { connectionId, masterPwd, tableName, userId } = inputData; - let { row } = inputData; - const connection = await this._dbContext.connectionRepository.findAndDecryptConnection(connectionId, masterPwd); - if (!connection) { - throw new HttpException( - { - message: Messages.CONNECTION_NOT_FOUND, - }, - HttpStatus.BAD_REQUEST, - ); - } - if (connection.is_frozen) { - throw new NonAvailableInFreePlanException(Messages.CONNECTION_IS_FROZEN); - } - let operationResult = OperationResultStatusEnum.unknown; + protected async implementation(inputData: AddRowInTableDs): Promise { + const { connectionId, masterPwd, tableName, userId } = inputData; + let { row } = inputData; + const connection = await this._dbContext.connectionRepository.findAndDecryptConnection(connectionId, masterPwd); + if (!connection) { + throw new HttpException( + { + message: Messages.CONNECTION_NOT_FOUND, + }, + HttpStatus.BAD_REQUEST, + ); + } + if (connection.is_frozen) { + throw new NonAvailableInFreePlanException(Messages.CONNECTION_IS_FROZEN); + } + let operationResult = OperationResultStatusEnum.unknown; - const dao = getDataAccessObject(connection); + const dao = getDataAccessObject(connection); - let userEmail: string; - if (isConnectionTypeAgent(connection.type)) { - userEmail = await this._dbContext.userRepository.getUserEmailOrReturnNull(userId); - } + let userEmail: string; + if (isConnectionTypeAgent(connection.type)) { + userEmail = await this._dbContext.userRepository.getUserEmailOrReturnNull(userId); + } - const tablesInConnection = await dao.getTablesFromDB(userEmail); - const isTableInConnection = tablesInConnection.some((el) => el.tableName === tableName); - if (!isTableInConnection) { - throw new HttpException( - { - message: Messages.TABLE_NOT_FOUND, - }, - HttpStatus.BAD_REQUEST, - ); - } + const tablesInConnection = await dao.getTablesFromDB(userEmail); + const isTableInConnection = tablesInConnection.some((el) => el.tableName === tableName); + if (!isTableInConnection) { + throw new HttpException( + { + message: Messages.TABLE_NOT_FOUND, + }, + HttpStatus.BAD_REQUEST, + ); + } - const isView = await dao.isView(tableName, userEmail); - if (isView) { - throw new HttpException( - { - message: Messages.CANT_UPDATE_TABLE_VIEW, - }, - HttpStatus.BAD_REQUEST, - ); - } + const isView = await dao.isView(tableName, userEmail); + if (isView) { + throw new HttpException( + { + message: Messages.CANT_UPDATE_TABLE_VIEW, + }, + HttpStatus.BAD_REQUEST, + ); + } - const [ - tableStructure, - tableWidgets, - tableSettings, - personalTableSettings, - tableForeignKeys, - tablePrimaryKeys, - referencedTableNamesAndColumns, - ] = await Promise.all([ - dao.getTableStructure(tableName, userEmail), - this._dbContext.tableWidgetsRepository.findTableWidgets(connectionId, tableName), - this._dbContext.tableSettingsRepository.findTableSettings(connectionId, tableName), - this._dbContext.personalTableSettingsRepository.findUserTableSettings(userId, connectionId, tableName), - dao.getTableForeignKeys(tableName, userEmail), - dao.getTablePrimaryColumns(tableName, userEmail), - dao.getReferencedTableNamesAndColumns(tableName, userEmail), - ]); + const [ + tableStructure, + tableWidgets, + tableSettings, + personalTableSettings, + tableForeignKeys, + tablePrimaryKeys, + referencedTableNamesAndColumns, + ] = await Promise.all([ + dao.getTableStructure(tableName, userEmail), + this._dbContext.tableWidgetsRepository.findTableWidgets(connectionId, tableName), + this._dbContext.tableSettingsRepository.findTableSettings(connectionId, tableName), + this._dbContext.personalTableSettingsRepository.findUserTableSettings(userId, connectionId, tableName), + dao.getTableForeignKeys(tableName, userEmail), + dao.getTablePrimaryColumns(tableName, userEmail), + dao.getReferencedTableNamesAndColumns(tableName, userEmail), + ]); - for (const referencedTable of referencedTableNamesAndColumns) { - referencedTable.referenced_by = await Promise.all( - referencedTable.referenced_by.map(async (referencedByTable) => { - const canUserReadTable = await this._dbContext.userAccessRepository.improvedCheckTableRead( - userId, - connectionId, - referencedByTable.table_name, - masterPwd, - ); - return canUserReadTable ? referencedByTable : null; - }), - ).then((results) => results.filter(Boolean)); - } + for (const referencedTable of referencedTableNamesAndColumns) { + referencedTable.referenced_by = await Promise.all( + referencedTable.referenced_by.map(async (referencedByTable) => { + const canUserReadTable = await this._dbContext.userAccessRepository.improvedCheckTableRead( + userId, + connectionId, + referencedByTable.table_name, + masterPwd, + ); + return canUserReadTable ? referencedByTable : null; + }), + ).then((results) => results.filter(Boolean)); + } - const referencedTableNamesAndColumnsWithTablesDisplayNames: Array = []; + const referencedTableNamesAndColumnsWithTablesDisplayNames: Array = []; - for (const el of referencedTableNamesAndColumns) { - const { referenced_by, referenced_on_column_name } = el; - const referenced_by_with_display_name = await Promise.all( - referenced_by.map(async (element) => { - const foundTableSettings = await this._dbContext.tableSettingsRepository.findTableSettings( - connectionId, - element.table_name, - ); - return { - ...element, - display_name: foundTableSettings?.display_name || null, - }; - }), - ); + for (const el of referencedTableNamesAndColumns) { + const { referenced_by, referenced_on_column_name } = el; + const referenced_by_with_display_name = await Promise.all( + referenced_by.map(async (element) => { + const foundTableSettings = await this._dbContext.tableSettingsRepository.findTableSettings( + connectionId, + element.table_name, + ); + return { + ...element, + display_name: foundTableSettings?.display_name || null, + }; + }), + ); - referencedTableNamesAndColumnsWithTablesDisplayNames.push({ - referenced_on_column_name, - referenced_by: referenced_by_with_display_name, - }); - } + referencedTableNamesAndColumnsWithTablesDisplayNames.push({ + referenced_on_column_name, + referenced_by: referenced_by_with_display_name, + }); + } - if (tableSettings && !tableSettings?.can_add) { - throw new HttpException( - { - message: Messages.CANT_DO_TABLE_OPERATION, - }, - HttpStatus.FORBIDDEN, - ); - } + if (tableSettings && !tableSettings?.can_add) { + throw new HttpException( + { + message: Messages.CANT_DO_TABLE_OPERATION, + }, + HttpStatus.FORBIDDEN, + ); + } - const foreignKeysFromWidgets: Array = tableWidgets - .filter((el) => el.widget_type === WidgetTypeEnum.Foreign_key) - .map((widget) => widget.widget_params as unknown as ForeignKeyDSInfo); + const foreignKeysFromWidgets: Array = tableWidgets + .filter((el) => el.widget_type === WidgetTypeEnum.Foreign_key) + .map((widget) => widget.widget_params as unknown as ForeignKeyDSInfo); - let foreignKeysWithKeysFromWidgets = [...tableForeignKeys, ...foreignKeysFromWidgets]; + let foreignKeysWithKeysFromWidgets = [...tableForeignKeys, ...foreignKeysFromWidgets]; - let foreignKeysWithAutocompleteColumns: Array = []; + let foreignKeysWithAutocompleteColumns: Array = []; - const canUserReadForeignTables = await Promise.all( - foreignKeysWithKeysFromWidgets.map(async (foreignKey) => { - const canRead = await this._dbContext.userAccessRepository.improvedCheckTableRead( - userId, - connectionId, - foreignKey.referenced_table_name, - masterPwd, - ); - return { - tableName: foreignKey.referenced_table_name, - canRead, - }; - }), - ); + const canUserReadForeignTables = await Promise.all( + foreignKeysWithKeysFromWidgets.map(async (foreignKey) => { + const canRead = await this._dbContext.userAccessRepository.improvedCheckTableRead( + userId, + connectionId, + foreignKey.referenced_table_name, + masterPwd, + ); + return { + tableName: foreignKey.referenced_table_name, + canRead, + }; + }), + ); - const canReadMap = new Map(canUserReadForeignTables.map((item) => [item.tableName, item.canRead])); + const canReadMap = new Map(canUserReadForeignTables.map((item) => [item.tableName, item.canRead])); - foreignKeysWithKeysFromWidgets = foreignKeysWithKeysFromWidgets.filter((foreignKey) => - canReadMap.get(foreignKey.referenced_table_name), - ); + foreignKeysWithKeysFromWidgets = foreignKeysWithKeysFromWidgets.filter((foreignKey) => + canReadMap.get(foreignKey.referenced_table_name), + ); - if (foreignKeysWithKeysFromWidgets?.length > 0) { - foreignKeysWithAutocompleteColumns = await Promise.all( - foreignKeysWithKeysFromWidgets.map(async (el) => { - try { - return await this.attachForeignColumnNames(el, userId, connectionId, dao); - } catch (_e) { - return el as ForeignKeyWithAutocompleteColumnsDS; - } - }), - ); - } + if (foreignKeysWithKeysFromWidgets?.length > 0) { + foreignKeysWithAutocompleteColumns = await Promise.all( + foreignKeysWithKeysFromWidgets.map(async (el) => { + try { + return await this.attachForeignColumnNames(el, userId, connectionId, dao); + } catch (_e) { + return el as ForeignKeyWithAutocompleteColumnsDS; + } + }), + ); + } - const errors = validateTableRowUtil(row, tableStructure); - if (errors.length > 0) { - throw new HttpException( - { - message: toPrettyErrorsMsg(errors), - }, - HttpStatus.BAD_REQUEST, - ); - } + const errors = validateTableRowUtil(row, tableStructure); + if (errors.length > 0) { + throw new HttpException( + { + message: toPrettyErrorsMsg(errors), + }, + HttpStatus.BAD_REQUEST, + ); + } - const formedTableStructure = formFullTableStructure(tableStructure, tableSettings); - let addedRow: Record = {}; - let addedRowPrimaryKey: Record; - const builtDAOsTableSettings = buildDAOsTableSettingsDs(tableSettings, personalTableSettings); - try { - row = await hashPasswordsInRowUtil(row, tableWidgets); - row = processUuidsInRowUtil(row, tableWidgets); - row = convertHexDataInRowUtil(row, tableStructure); - addedRowPrimaryKey = (await dao.addRowInTable(tableName, row, userEmail)) as Record; - if (addedRowPrimaryKey && !isObjectEmpty(addedRowPrimaryKey)) { - operationResult = OperationResultStatusEnum.successfully; - addedRow = await dao.getRowByPrimaryKey(tableName, addedRowPrimaryKey, builtDAOsTableSettings, userEmail); - addedRow = removePasswordsFromRowsUtil(addedRow, tableWidgets); - addedRow = convertBinaryDataInRowUtil(addedRow, tableStructure); - return { - row: addedRow, - foreignKeys: foreignKeysWithAutocompleteColumns, - primaryColumns: tablePrimaryKeys, - structure: formedTableStructure, - table_widgets: tableWidgets, - display_name: tableSettings?.display_name ? tableSettings.display_name : null, - readonly_fields: tableSettings?.readonly_fields ? tableSettings.readonly_fields : [], - list_fields: personalTableSettings?.list_fields?.length > 0 ? personalTableSettings.list_fields : [], - identity_column: tableSettings?.identity_column ? tableSettings.identity_column : null, - referenced_table_names_and_columns: referencedTableNamesAndColumnsWithTablesDisplayNames, - excluded_fields: tableSettings?.excluded_fields ? tableSettings.excluded_fields : [], - can_delete: tableSettings ? tableSettings.can_delete : true, - can_update: tableSettings ? tableSettings.can_update : true, - can_add: tableSettings ? tableSettings.can_add : true, - table_settings: { - sortable_by: tableSettings?.sortable_by?.length > 0 ? tableSettings.sortable_by : [], - ordering: personalTableSettings?.ordering ? personalTableSettings.ordering : undefined, - identity_column: tableSettings?.identity_column ? tableSettings.identity_column : null, - list_fields: personalTableSettings?.list_fields?.length > 0 ? personalTableSettings.list_fields : [], - allow_csv_export: tableSettings ? tableSettings.allow_csv_export : true, - allow_csv_import: tableSettings ? tableSettings.allow_csv_import : true, - can_delete: tableSettings ? tableSettings.can_delete : true, - can_update: tableSettings ? tableSettings.can_update : true, - can_add: tableSettings ? tableSettings.can_add : true, - }, - }; - } - } catch (e) { - operationResult = OperationResultStatusEnum.unsuccessfully; - throw new HttpException( - { - message: e.message.includes('duplicate key value') - ? Messages.CANT_INSERT_DUPLICATE_KEY - : `${Messages.FAILED_ADD_ROW_IN_TABLE} + const formedTableStructure = formFullTableStructure(tableStructure, tableSettings); + let addedRow: Record = {}; + let addedRowPrimaryKey: Record; + const builtDAOsTableSettings = buildDAOsTableSettingsDs(tableSettings, personalTableSettings); + try { + row = await hashPasswordsInRowUtil(row, tableWidgets); + row = processUuidsInRowUtil(row, tableWidgets); + row = convertHexDataInRowUtil(row, tableStructure); + addedRowPrimaryKey = (await dao.addRowInTable(tableName, row, userEmail)) as Record; + if (addedRowPrimaryKey && !isObjectEmpty(addedRowPrimaryKey)) { + operationResult = OperationResultStatusEnum.successfully; + addedRow = await dao.getRowByPrimaryKey(tableName, addedRowPrimaryKey, builtDAOsTableSettings, userEmail); + addedRow = removePasswordsFromRowsUtil(addedRow, tableWidgets); + addedRow = convertBinaryDataInRowUtil(addedRow, tableStructure); + return { + row: addedRow, + foreignKeys: foreignKeysWithAutocompleteColumns, + primaryColumns: tablePrimaryKeys, + structure: formedTableStructure, + table_widgets: tableWidgets, + display_name: tableSettings?.display_name ? tableSettings.display_name : null, + readonly_fields: tableSettings?.readonly_fields ? tableSettings.readonly_fields : [], + list_fields: personalTableSettings?.list_fields?.length > 0 ? personalTableSettings.list_fields : [], + identity_column: tableSettings?.identity_column ? tableSettings.identity_column : null, + referenced_table_names_and_columns: referencedTableNamesAndColumnsWithTablesDisplayNames, + excluded_fields: tableSettings?.excluded_fields ? tableSettings.excluded_fields : [], + can_delete: tableSettings ? tableSettings.can_delete : true, + can_update: tableSettings ? tableSettings.can_update : true, + can_add: tableSettings ? tableSettings.can_add : true, + table_settings: { + sortable_by: tableSettings?.sortable_by?.length > 0 ? tableSettings.sortable_by : [], + ordering: personalTableSettings?.ordering ? personalTableSettings.ordering : undefined, + identity_column: tableSettings?.identity_column ? tableSettings.identity_column : null, + list_fields: personalTableSettings?.list_fields?.length > 0 ? personalTableSettings.list_fields : [], + allow_csv_export: tableSettings ? tableSettings.allow_csv_export : true, + allow_csv_import: tableSettings ? tableSettings.allow_csv_import : true, + can_delete: tableSettings ? tableSettings.can_delete : true, + can_update: tableSettings ? tableSettings.can_update : true, + can_add: tableSettings ? tableSettings.can_add : true, + ordering_field: personalTableSettings?.ordering_field ? personalTableSettings.ordering_field : undefined, + }, + }; + } + } catch (e) { + operationResult = OperationResultStatusEnum.unsuccessfully; + throw new HttpException( + { + message: e.message.includes('duplicate key value') + ? Messages.CANT_INSERT_DUPLICATE_KEY + : `${Messages.FAILED_ADD_ROW_IN_TABLE} ${Messages.ERROR_MESSAGE} ${e.message} ${Messages.TRY_AGAIN_LATER}`, - }, - HttpStatus.BAD_REQUEST, - ); - } finally { - const logRecord = { - table_name: tableName, - userId: userId, - connection: connection, - operationType: LogOperationTypeEnum.addRow, - operationStatusResult: operationResult, - row: row, - affected_primary_key: addedRowPrimaryKey, - }; - await this.tableLogsService.crateAndSaveNewLogUtil(logRecord); - const isTest = isTestConnectionUtil(connection); - await this.amplitudeService.formAndSendLogRecord( - isTest ? AmplitudeEventTypeEnum.tableRowAddedTest : AmplitudeEventTypeEnum.tableRowAdded, - userId, - ); + }, + HttpStatus.BAD_REQUEST, + ); + } finally { + const logRecord = { + table_name: tableName, + userId: userId, + connection: connection, + operationType: LogOperationTypeEnum.addRow, + operationStatusResult: operationResult, + row: row, + affected_primary_key: addedRowPrimaryKey, + }; + await this.tableLogsService.crateAndSaveNewLogUtil(logRecord); + const isTest = isTestConnectionUtil(connection); + await this.amplitudeService.formAndSendLogRecord( + isTest ? AmplitudeEventTypeEnum.tableRowAddedTest : AmplitudeEventTypeEnum.tableRowAdded, + userId, + ); - const foundAddTableActions = await this._dbContext.tableActionRepository.findTableActionsWithAddRowEvents( - connectionId, - tableName, - ); + const foundAddTableActions = await this._dbContext.tableActionRepository.findTableActionsWithAddRowEvents( + connectionId, + tableName, + ); - await this.tableActionActivationService.activateTableActions( - foundAddTableActions, - connection, - addedRow, - userId, - tableName, - TableActionEventEnum.ADD_ROW, - ); - } - } + await this.tableActionActivationService.activateTableActions( + foundAddTableActions, + connection, + addedRow, + userId, + tableName, + TableActionEventEnum.ADD_ROW, + ); + } + } - private async attachForeignColumnNames( - foreignKey: ForeignKeyDS, - userId: string, - connectionId: string, - dao: IDataAccessObject | IDataAccessObjectAgent, - ): Promise { - try { - const foreignTableSettings = await this._dbContext.tableSettingsRepository.findTableSettings( - connectionId, - foreignKey.referenced_table_name, - ); - const foreignTableStructure = await dao.getTableStructure(foreignKey.referenced_table_name, userId); + private async attachForeignColumnNames( + foreignKey: ForeignKeyDS, + userId: string, + connectionId: string, + dao: IDataAccessObject | IDataAccessObjectAgent, + ): Promise { + try { + const foreignTableSettings = await this._dbContext.tableSettingsRepository.findTableSettings( + connectionId, + foreignKey.referenced_table_name, + ); + const foreignTableStructure = await dao.getTableStructure(foreignKey.referenced_table_name, userId); - const columnNames = foreignTableStructure - .map((el) => el.column_name) - .filter((el) => foreignTableSettings?.autocomplete_columns.includes(el)); + const columnNames = foreignTableStructure + .map((el) => el.column_name) + .filter((el) => foreignTableSettings?.autocomplete_columns.includes(el)); - return { - ...foreignKey, - autocomplete_columns: columnNames, - }; - } catch (_e) { - return { - ...foreignKey, - autocomplete_columns: [], - }; - } - } + return { + ...foreignKey, + autocomplete_columns: columnNames, + }; + } catch (_e) { + return { + ...foreignKey, + autocomplete_columns: [], + }; + } + } } diff --git a/backend/src/entities/table/use-cases/get-row-by-primary-key.use.case.ts b/backend/src/entities/table/use-cases/get-row-by-primary-key.use.case.ts index 70315a171..bf3a878f0 100644 --- a/backend/src/entities/table/use-cases/get-row-by-primary-key.use.case.ts +++ b/backend/src/entities/table/use-cases/get-row-by-primary-key.use.case.ts @@ -29,253 +29,254 @@ import { buildDAOsTableSettingsDs } from '@rocketadmin/shared-code/dist/src/help @Injectable() export class GetRowByPrimaryKeyUseCase - extends AbstractUseCase - implements IGetRowByPrimaryKey + extends AbstractUseCase + implements IGetRowByPrimaryKey { - constructor( - @Inject(BaseType.GLOBAL_DB_CONTEXT) - protected _dbContext: IGlobalDatabaseContext, - ) { - super(); - } + constructor( + @Inject(BaseType.GLOBAL_DB_CONTEXT) + protected _dbContext: IGlobalDatabaseContext, + ) { + super(); + } - protected async implementation(inputData: GetRowByPrimaryKeyDs): Promise { - let { connectionId, masterPwd, primaryKey, tableName, userId } = inputData; - if (!primaryKey) { - throw new HttpException( - { - message: Messages.PRIMARY_KEY_MISSING, - }, - HttpStatus.BAD_REQUEST, - ); - } + protected async implementation(inputData: GetRowByPrimaryKeyDs): Promise { + let { connectionId, masterPwd, primaryKey, tableName, userId } = inputData; + if (!primaryKey) { + throw new HttpException( + { + message: Messages.PRIMARY_KEY_MISSING, + }, + HttpStatus.BAD_REQUEST, + ); + } - const connection = await this._dbContext.connectionRepository.findAndDecryptConnection(connectionId, masterPwd); - if (!connection) { - throw new HttpException( - { - message: Messages.CONNECTION_NOT_FOUND, - }, - HttpStatus.BAD_REQUEST, - ); - } + const connection = await this._dbContext.connectionRepository.findAndDecryptConnection(connectionId, masterPwd); + if (!connection) { + throw new HttpException( + { + message: Messages.CONNECTION_NOT_FOUND, + }, + HttpStatus.BAD_REQUEST, + ); + } - if (connection.is_frozen) { - throw new NonAvailableInFreePlanException(Messages.CONNECTION_IS_FROZEN); - } + if (connection.is_frozen) { + throw new NonAvailableInFreePlanException(Messages.CONNECTION_IS_FROZEN); + } - const dao = getDataAccessObject(connection); + const dao = getDataAccessObject(connection); - let userEmail: string; - if (isConnectionTypeAgent(connection.type)) { - userEmail = await this._dbContext.userRepository.getUserEmailOrReturnNull(userId); - } - let [ - tableStructure, - tableWidgets, - tableSettings, - personalTableSettings, - tableForeignKeys, - tablePrimaryKeys, - customActionEvents, - referencedTableNamesAndColumns, - tableAccessLevel, - ] = await Promise.all([ - dao.getTableStructure(tableName, userEmail), - this._dbContext.tableWidgetsRepository.findTableWidgets(connectionId, tableName), - this._dbContext.tableSettingsRepository.findTableSettingsPure(connectionId, tableName), - this._dbContext.personalTableSettingsRepository.findUserTableSettings(userId, connectionId, tableName), - dao.getTableForeignKeys(tableName, userEmail), - dao.getTablePrimaryColumns(tableName, userEmail), - this._dbContext.actionEventsRepository.findCustomEventsForTable(connectionId, tableName), - dao.getReferencedTableNamesAndColumns(tableName, userEmail), - this._dbContext.userAccessRepository.getUserTablePermissions(userId, connectionId, tableName, masterPwd), - ]); - primaryKey = convertHexDataInPrimaryKeyUtil(primaryKey, tableStructure); - const availablePrimaryColumns: Array = tablePrimaryKeys.map((column) => column.column_name); - for (const key in primaryKey) { - // eslint-disable-next-line security/detect-object-injection - if (!primaryKey[key] && primaryKey[key] !== '') delete primaryKey[key]; - } - const receivedPrimaryColumns = Object.keys(primaryKey); - if (!compareArrayElements(availablePrimaryColumns, receivedPrimaryColumns)) { - throw new HttpException( - { - message: Messages.PRIMARY_KEY_INVALID, - }, - HttpStatus.BAD_REQUEST, - ); - } + let userEmail: string; + if (isConnectionTypeAgent(connection.type)) { + userEmail = await this._dbContext.userRepository.getUserEmailOrReturnNull(userId); + } + let [ + tableStructure, + tableWidgets, + tableSettings, + personalTableSettings, + tableForeignKeys, + tablePrimaryKeys, + customActionEvents, + referencedTableNamesAndColumns, + tableAccessLevel, + ] = await Promise.all([ + dao.getTableStructure(tableName, userEmail), + this._dbContext.tableWidgetsRepository.findTableWidgets(connectionId, tableName), + this._dbContext.tableSettingsRepository.findTableSettingsPure(connectionId, tableName), + this._dbContext.personalTableSettingsRepository.findUserTableSettings(userId, connectionId, tableName), + dao.getTableForeignKeys(tableName, userEmail), + dao.getTablePrimaryColumns(tableName, userEmail), + this._dbContext.actionEventsRepository.findCustomEventsForTable(connectionId, tableName), + dao.getReferencedTableNamesAndColumns(tableName, userEmail), + this._dbContext.userAccessRepository.getUserTablePermissions(userId, connectionId, tableName, masterPwd), + ]); + primaryKey = convertHexDataInPrimaryKeyUtil(primaryKey, tableStructure); + const availablePrimaryColumns: Array = tablePrimaryKeys.map((column) => column.column_name); + for (const key in primaryKey) { + // eslint-disable-next-line security/detect-object-injection + if (!primaryKey[key] && primaryKey[key] !== '') delete primaryKey[key]; + } + const receivedPrimaryColumns = Object.keys(primaryKey); + if (!compareArrayElements(availablePrimaryColumns, receivedPrimaryColumns)) { + throw new HttpException( + { + message: Messages.PRIMARY_KEY_INVALID, + }, + HttpStatus.BAD_REQUEST, + ); + } - const foreignKeysFromWidgets: Array = tableWidgets - .filter((widget) => widget.widget_type === WidgetTypeEnum.Foreign_key) - .map((widget) => { - if (widget.widget_params) { - try { - const widgetParams = JSON5.parse(widget.widget_params) as ForeignKeyDSInfo; - return widgetParams; - } catch (_e) { - return null; - } - } - }) - .filter((el) => el !== null); + const foreignKeysFromWidgets: Array = tableWidgets + .filter((widget) => widget.widget_type === WidgetTypeEnum.Foreign_key) + .map((widget) => { + if (widget.widget_params) { + try { + const widgetParams = JSON5.parse(widget.widget_params) as ForeignKeyDSInfo; + return widgetParams; + } catch (_e) { + return null; + } + } + }) + .filter((el) => el !== null); - tableForeignKeys = tableForeignKeys.concat(foreignKeysFromWidgets); - const canUserReadForeignTables: Array<{ - tableName: string; - canRead: boolean; - }> = await Promise.all( - tableForeignKeys.map(async (foreignKey) => { - const cenTableRead = await this._dbContext.userAccessRepository.improvedCheckTableRead( - userId, - connectionId, - foreignKey.referenced_table_name, - masterPwd, - ); - return { - tableName: foreignKey.referenced_table_name, - canRead: cenTableRead, - }; - }), - ); - tableForeignKeys = tableForeignKeys.filter((foreignKey) => { - return canUserReadForeignTables.find((el) => { - return el.tableName === foreignKey.referenced_table_name && el.canRead; - }); - }); + tableForeignKeys = tableForeignKeys.concat(foreignKeysFromWidgets); + const canUserReadForeignTables: Array<{ + tableName: string; + canRead: boolean; + }> = await Promise.all( + tableForeignKeys.map(async (foreignKey) => { + const cenTableRead = await this._dbContext.userAccessRepository.improvedCheckTableRead( + userId, + connectionId, + foreignKey.referenced_table_name, + masterPwd, + ); + return { + tableName: foreignKey.referenced_table_name, + canRead: cenTableRead, + }; + }), + ); + tableForeignKeys = tableForeignKeys.filter((foreignKey) => { + return canUserReadForeignTables.find((el) => { + return el.tableName === foreignKey.referenced_table_name && el.canRead; + }); + }); - let foreignKeysWithAutocompleteColumns: Array = []; - if (tableForeignKeys && tableForeignKeys.length > 0) { - foreignKeysWithAutocompleteColumns = await Promise.all( - tableForeignKeys.map((el) => { - try { - return this.attachForeignColumnNames(el, userId, connectionId, dao); - } catch (_e) { - return el as ForeignKeyWithAutocompleteColumnsDS; - } - }), - ); - } - let rowData: Record; - const builtDAOsTableSettings = buildDAOsTableSettingsDs(tableSettings, personalTableSettings); - try { - rowData = await dao.getRowByPrimaryKey(tableName, primaryKey, builtDAOsTableSettings, userEmail); - } catch (e) { - throw new UnknownSQLException(e.message, ExceptionOperations.FAILED_TO_GET_ROW_BY_PRIMARY_KEY); - } + let foreignKeysWithAutocompleteColumns: Array = []; + if (tableForeignKeys && tableForeignKeys.length > 0) { + foreignKeysWithAutocompleteColumns = await Promise.all( + tableForeignKeys.map((el) => { + try { + return this.attachForeignColumnNames(el, userId, connectionId, dao); + } catch (_e) { + return el as ForeignKeyWithAutocompleteColumnsDS; + } + }), + ); + } + let rowData: Record; + const builtDAOsTableSettings = buildDAOsTableSettingsDs(tableSettings, personalTableSettings); + try { + rowData = await dao.getRowByPrimaryKey(tableName, primaryKey, builtDAOsTableSettings, userEmail); + } catch (e) { + throw new UnknownSQLException(e.message, ExceptionOperations.FAILED_TO_GET_ROW_BY_PRIMARY_KEY); + } - if (!rowData) { - throw new HttpException( - { - message: Messages.ROW_PRIMARY_KEY_NOT_FOUND, - }, - HttpStatus.BAD_REQUEST, - ); - } - rowData = removePasswordsFromRowsUtil(rowData, tableWidgets); - rowData = convertBinaryDataInRowUtil(rowData, tableStructure); - const formedTableStructure = formFullTableStructure(tableStructure, tableSettings); + if (!rowData) { + throw new HttpException( + { + message: Messages.ROW_PRIMARY_KEY_NOT_FOUND, + }, + HttpStatus.BAD_REQUEST, + ); + } + rowData = removePasswordsFromRowsUtil(rowData, tableWidgets); + rowData = convertBinaryDataInRowUtil(rowData, tableStructure); + const formedTableStructure = formFullTableStructure(tableStructure, tableSettings); - for (const referencedTable of referencedTableNamesAndColumns) { - referencedTable.referenced_by = await Promise.all( - referencedTable.referenced_by.map(async (referencedByTable) => { - const canUserReadTable = await this._dbContext.userAccessRepository.improvedCheckTableRead( - userId, - connectionId, - referencedByTable.table_name, - masterPwd, - ); - return canUserReadTable ? referencedByTable : null; - }), - ).then((results) => results.filter(Boolean)); - } + for (const referencedTable of referencedTableNamesAndColumns) { + referencedTable.referenced_by = await Promise.all( + referencedTable.referenced_by.map(async (referencedByTable) => { + const canUserReadTable = await this._dbContext.userAccessRepository.improvedCheckTableRead( + userId, + connectionId, + referencedByTable.table_name, + masterPwd, + ); + return canUserReadTable ? referencedByTable : null; + }), + ).then((results) => results.filter(Boolean)); + } - const referencedTableNamesAndColumnsWithTablesDisplayNames: Array = - await Promise.all( - referencedTableNamesAndColumns.map(async (el: ReferencedTableNamesAndColumnsDS) => { - const { referenced_by, referenced_on_column_name } = el; - const responseObject: ReferencedTableNamesAndColumnsDs = { - referenced_on_column_name: referenced_on_column_name, - referenced_by: [], - }; - for (const element of referenced_by) { - const foundTableSettings = await this._dbContext.tableSettingsRepository.findTableSettings( - connectionId, - element.table_name, - ); - const displayName = foundTableSettings?.display_name ? foundTableSettings.display_name : null; - responseObject.referenced_by.push({ - ...element, - display_name: displayName, - }); - } - return responseObject; - }), - ); - //todo remove unnecessary fields - return { - row: rowData, - foreignKeys: foreignKeysWithAutocompleteColumns, - primaryColumns: tablePrimaryKeys, - structure: formedTableStructure, - table_widgets: tableWidgets, - readonly_fields: tableSettings?.readonly_fields ? tableSettings.readonly_fields : [], - list_fields: findAvailableFields(builtDAOsTableSettings, tableStructure), - action_events: customActionEvents.map((event) => buildActionEventDto(event)), - table_actions: customActionEvents.map((el) => buildActionEventDto(el)), - identity_column: tableSettings?.identity_column ? tableSettings.identity_column : null, - referenced_table_names_and_columns: referencedTableNamesAndColumnsWithTablesDisplayNames, - display_name: tableSettings?.display_name ? tableSettings.display_name : null, - table_access_level: tableAccessLevel.accessLevel, - excluded_fields: tableSettings?.excluded_fields ? tableSettings.excluded_fields : [], - can_delete: tableSettings ? tableSettings.can_delete : true, - can_update: tableSettings ? tableSettings.can_update : true, - can_add: tableSettings ? tableSettings.can_add : true, - table_settings: { - sortable_by: tableSettings?.sortable_by?.length > 0 ? tableSettings.sortable_by : [], - ordering: personalTableSettings?.ordering ? personalTableSettings.ordering : undefined, - identity_column: tableSettings?.identity_column ? tableSettings.identity_column : null, - list_fields: personalTableSettings?.list_fields?.length > 0 ? personalTableSettings.list_fields : [], - allow_csv_export: tableSettings ? tableSettings.allow_csv_export : true, - allow_csv_import: tableSettings ? tableSettings.allow_csv_import : true, - can_delete: tableSettings ? tableSettings.can_delete : true, - can_update: tableSettings ? tableSettings.can_update : true, - can_add: tableSettings ? tableSettings.can_add : true, - }, - }; - } + const referencedTableNamesAndColumnsWithTablesDisplayNames: Array = + await Promise.all( + referencedTableNamesAndColumns.map(async (el: ReferencedTableNamesAndColumnsDS) => { + const { referenced_by, referenced_on_column_name } = el; + const responseObject: ReferencedTableNamesAndColumnsDs = { + referenced_on_column_name: referenced_on_column_name, + referenced_by: [], + }; + for (const element of referenced_by) { + const foundTableSettings = await this._dbContext.tableSettingsRepository.findTableSettings( + connectionId, + element.table_name, + ); + const displayName = foundTableSettings?.display_name ? foundTableSettings.display_name : null; + responseObject.referenced_by.push({ + ...element, + display_name: displayName, + }); + } + return responseObject; + }), + ); + //todo remove unnecessary fields + return { + row: rowData, + foreignKeys: foreignKeysWithAutocompleteColumns, + primaryColumns: tablePrimaryKeys, + structure: formedTableStructure, + table_widgets: tableWidgets, + readonly_fields: tableSettings?.readonly_fields ? tableSettings.readonly_fields : [], + list_fields: findAvailableFields(builtDAOsTableSettings, tableStructure), + action_events: customActionEvents.map((event) => buildActionEventDto(event)), + table_actions: customActionEvents.map((el) => buildActionEventDto(el)), + identity_column: tableSettings?.identity_column ? tableSettings.identity_column : null, + referenced_table_names_and_columns: referencedTableNamesAndColumnsWithTablesDisplayNames, + display_name: tableSettings?.display_name ? tableSettings.display_name : null, + table_access_level: tableAccessLevel.accessLevel, + excluded_fields: tableSettings?.excluded_fields ? tableSettings.excluded_fields : [], + can_delete: tableSettings ? tableSettings.can_delete : true, + can_update: tableSettings ? tableSettings.can_update : true, + can_add: tableSettings ? tableSettings.can_add : true, + table_settings: { + sortable_by: tableSettings?.sortable_by?.length > 0 ? tableSettings.sortable_by : [], + ordering: personalTableSettings?.ordering ? personalTableSettings.ordering : undefined, + identity_column: tableSettings?.identity_column ? tableSettings.identity_column : null, + list_fields: personalTableSettings?.list_fields?.length > 0 ? personalTableSettings.list_fields : [], + allow_csv_export: tableSettings ? tableSettings.allow_csv_export : true, + allow_csv_import: tableSettings ? tableSettings.allow_csv_import : true, + can_delete: tableSettings ? tableSettings.can_delete : true, + can_update: tableSettings ? tableSettings.can_update : true, + can_add: tableSettings ? tableSettings.can_add : true, + ordering_field: personalTableSettings?.ordering_field ? personalTableSettings.ordering_field : undefined, + }, + }; + } - private async attachForeignColumnNames( - foreignKey: ForeignKeyDS, - userId: string, - connectionId: string, - dao: IDataAccessObject | IDataAccessObjectAgent, - ): Promise { - try { - const [foreignTableSettings, foreignTableStructure] = await Promise.all([ - this._dbContext.tableSettingsRepository.findTableSettings(connectionId, foreignKey.referenced_table_name), - dao.getTableStructure(foreignKey.referenced_table_name, userId), - ]); + private async attachForeignColumnNames( + foreignKey: ForeignKeyDS, + userId: string, + connectionId: string, + dao: IDataAccessObject | IDataAccessObjectAgent, + ): Promise { + try { + const [foreignTableSettings, foreignTableStructure] = await Promise.all([ + this._dbContext.tableSettingsRepository.findTableSettings(connectionId, foreignKey.referenced_table_name), + dao.getTableStructure(foreignKey.referenced_table_name, userId), + ]); - let columnNames = foreignTableStructure.map((el) => { - return el.column_name; - }); - if (foreignTableSettings && foreignTableSettings.autocomplete_columns.length > 0) { - columnNames = columnNames.filter((el) => { - const index = foreignTableSettings.autocomplete_columns.indexOf(el); - return index >= 0; - }); - } - return { - ...foreignKey, - autocomplete_columns: columnNames, - }; - } catch (_e) { - return { - ...foreignKey, - autocomplete_columns: [], - }; - } - } + let columnNames = foreignTableStructure.map((el) => { + return el.column_name; + }); + if (foreignTableSettings && foreignTableSettings.autocomplete_columns.length > 0) { + columnNames = columnNames.filter((el) => { + const index = foreignTableSettings.autocomplete_columns.indexOf(el); + return index >= 0; + }); + } + return { + ...foreignKey, + autocomplete_columns: columnNames, + }; + } catch (_e) { + return { + ...foreignKey, + autocomplete_columns: [], + }; + } + } } diff --git a/backend/src/entities/table/use-cases/get-table-rows.use.case.ts b/backend/src/entities/table/use-cases/get-table-rows.use.case.ts index b98e8db80..45c46255d 100644 --- a/backend/src/entities/table/use-cases/get-table-rows.use.case.ts +++ b/backend/src/entities/table/use-cases/get-table-rows.use.case.ts @@ -259,6 +259,7 @@ export class GetTableRowsUseCase extends AbstractUseCase - implements IUpdateRowInTable + extends AbstractUseCase + implements IUpdateRowInTable { - constructor( - @Inject(BaseType.GLOBAL_DB_CONTEXT) - protected _dbContext: IGlobalDatabaseContext, - private amplitudeService: AmplitudeService, - private tableLogsService: TableLogsService, - private tableActionActivationService: TableActionActivationService, - ) { - super(); - } + constructor( + @Inject(BaseType.GLOBAL_DB_CONTEXT) + protected _dbContext: IGlobalDatabaseContext, + private amplitudeService: AmplitudeService, + private tableLogsService: TableLogsService, + private tableActionActivationService: TableActionActivationService, + ) { + super(); + } - protected async implementation(inputData: UpdateRowInTableDs): Promise { - let { connectionId, masterPwd, primaryKey, row, tableName, userId } = inputData; - let operationResult = OperationResultStatusEnum.unknown; + protected async implementation(inputData: UpdateRowInTableDs): Promise { + let { connectionId, masterPwd, primaryKey, row, tableName, userId } = inputData; + let operationResult = OperationResultStatusEnum.unknown; - const errors = []; - if (!primaryKey) { - errors.push(Messages.PRIMARY_KEY_MISSING); - } + const errors = []; + if (!primaryKey) { + errors.push(Messages.PRIMARY_KEY_MISSING); + } - const connection = await this._dbContext.connectionRepository.findAndDecryptConnection(connectionId, masterPwd); - if (!connection) { - throw new HttpException( - { - message: Messages.CONNECTION_NOT_FOUND, - }, - HttpStatus.BAD_REQUEST, - ); - } + const connection = await this._dbContext.connectionRepository.findAndDecryptConnection(connectionId, masterPwd); + if (!connection) { + throw new HttpException( + { + message: Messages.CONNECTION_NOT_FOUND, + }, + HttpStatus.BAD_REQUEST, + ); + } - if (connection.is_frozen) { - throw new NonAvailableInFreePlanException(Messages.CONNECTION_IS_FROZEN); - } + if (connection.is_frozen) { + throw new NonAvailableInFreePlanException(Messages.CONNECTION_IS_FROZEN); + } - const dao = getDataAccessObject(connection); + const dao = getDataAccessObject(connection); - let userEmail: string; - if (isConnectionTypeAgent(connection.type)) { - userEmail = await this._dbContext.userRepository.getUserEmailOrReturnNull(userId); - } - const isView = await dao.isView(tableName, userEmail); - if (isView) { - throw new HttpException( - { - message: Messages.CANT_UPDATE_TABLE_VIEW, - }, - HttpStatus.BAD_REQUEST, - ); - } + let userEmail: string; + if (isConnectionTypeAgent(connection.type)) { + userEmail = await this._dbContext.userRepository.getUserEmailOrReturnNull(userId); + } + const isView = await dao.isView(tableName, userEmail); + if (isView) { + throw new HttpException( + { + message: Messages.CANT_UPDATE_TABLE_VIEW, + }, + HttpStatus.BAD_REQUEST, + ); + } - let [ - tableStructure, - tableWidgets, - tableSettings, - personalTableSettings, - tableForeignKeys, - tablePrimaryKeys, - referencedTableNamesAndColumns, - ] = await Promise.all([ - dao.getTableStructure(tableName, userEmail), - this._dbContext.tableWidgetsRepository.findTableWidgets(connectionId, tableName), - this._dbContext.tableSettingsRepository.findTableSettings(connectionId, tableName), - this._dbContext.personalTableSettingsRepository.findUserTableSettings(userId, connectionId, tableName), - dao.getTableForeignKeys(tableName, userEmail), - dao.getTablePrimaryColumns(tableName, userEmail), - dao.getReferencedTableNamesAndColumns(tableName, userEmail), - ]); + let [ + tableStructure, + tableWidgets, + tableSettings, + personalTableSettings, + tableForeignKeys, + tablePrimaryKeys, + referencedTableNamesAndColumns, + ] = await Promise.all([ + dao.getTableStructure(tableName, userEmail), + this._dbContext.tableWidgetsRepository.findTableWidgets(connectionId, tableName), + this._dbContext.tableSettingsRepository.findTableSettings(connectionId, tableName), + this._dbContext.personalTableSettingsRepository.findUserTableSettings(userId, connectionId, tableName), + dao.getTableForeignKeys(tableName, userEmail), + dao.getTablePrimaryColumns(tableName, userEmail), + dao.getReferencedTableNamesAndColumns(tableName, userEmail), + ]); - const builtDAOsTableSettings = buildDAOsTableSettingsDs(tableSettings, personalTableSettings); + const builtDAOsTableSettings = buildDAOsTableSettingsDs(tableSettings, personalTableSettings); - for (const referencedTable of referencedTableNamesAndColumns) { - referencedTable.referenced_by = await Promise.all( - referencedTable.referenced_by.map(async (referencedByTable) => { - const canUserReadTable = await this._dbContext.userAccessRepository.improvedCheckTableRead( - userId, - connectionId, - referencedByTable.table_name, - masterPwd, - ); - return canUserReadTable ? referencedByTable : null; - }), - ).then((results) => results.filter(Boolean)); - } + for (const referencedTable of referencedTableNamesAndColumns) { + referencedTable.referenced_by = await Promise.all( + referencedTable.referenced_by.map(async (referencedByTable) => { + const canUserReadTable = await this._dbContext.userAccessRepository.improvedCheckTableRead( + userId, + connectionId, + referencedByTable.table_name, + masterPwd, + ); + return canUserReadTable ? referencedByTable : null; + }), + ).then((results) => results.filter(Boolean)); + } - const referencedTableNamesAndColumnsWithTablesDisplayNames: Array = - await Promise.all( - referencedTableNamesAndColumns.map(async (el: ReferencedTableNamesAndColumnsDS) => { - const { referenced_by, referenced_on_column_name } = el; - const responseObject: ReferencedTableNamesAndColumnsDs = { - referenced_on_column_name: referenced_on_column_name, - referenced_by: [], - }; - for (const element of referenced_by) { - const foundTableSettings = await this._dbContext.tableSettingsRepository.findTableSettings( - connectionId, - element.table_name, - ); - const displayName = foundTableSettings?.display_name ? foundTableSettings.display_name : null; - responseObject.referenced_by.push({ - ...element, - display_name: displayName, - }); - } - return responseObject; - }), - ); + const referencedTableNamesAndColumnsWithTablesDisplayNames: Array = + await Promise.all( + referencedTableNamesAndColumns.map(async (el: ReferencedTableNamesAndColumnsDS) => { + const { referenced_by, referenced_on_column_name } = el; + const responseObject: ReferencedTableNamesAndColumnsDs = { + referenced_on_column_name: referenced_on_column_name, + referenced_by: [], + }; + for (const element of referenced_by) { + const foundTableSettings = await this._dbContext.tableSettingsRepository.findTableSettings( + connectionId, + element.table_name, + ); + const displayName = foundTableSettings?.display_name ? foundTableSettings.display_name : null; + responseObject.referenced_by.push({ + ...element, + display_name: displayName, + }); + } + return responseObject; + }), + ); - if (tableSettings && !tableSettings?.can_update) { - throw new HttpException( - { - message: Messages.CANT_DO_TABLE_OPERATION, - }, - HttpStatus.FORBIDDEN, - ); - } + if (tableSettings && !tableSettings?.can_update) { + throw new HttpException( + { + message: Messages.CANT_DO_TABLE_OPERATION, + }, + HttpStatus.FORBIDDEN, + ); + } - if (errors.length > 0) { - throw new HttpException( - { - message: toPrettyErrorsMsg(errors), - }, - HttpStatus.BAD_REQUEST, - ); - } + if (errors.length > 0) { + throw new HttpException( + { + message: toPrettyErrorsMsg(errors), + }, + HttpStatus.BAD_REQUEST, + ); + } - const foreignKeysFromWidgets: Array = tableWidgets - .filter((widget) => widget.widget_type === WidgetTypeEnum.Foreign_key) - .map((widget) => { - if (widget.widget_params) { - try { - const widgetParams = JSON5.parse(widget.widget_params) as ForeignKeyDSInfo; - return widgetParams; - } catch (_e) { - return null; - } - } - }) - .filter((el) => el !== null); + const foreignKeysFromWidgets: Array = tableWidgets + .filter((widget) => widget.widget_type === WidgetTypeEnum.Foreign_key) + .map((widget) => { + if (widget.widget_params) { + try { + const widgetParams = JSON5.parse(widget.widget_params) as ForeignKeyDSInfo; + return widgetParams; + } catch (_e) { + return null; + } + } + }) + .filter((el) => el !== null); - tableForeignKeys = tableForeignKeys.concat(foreignKeysFromWidgets); + tableForeignKeys = tableForeignKeys.concat(foreignKeysFromWidgets); - let foreignKeysWithAutocompleteColumns: Array = []; - const canUserReadForeignTables: Array<{ - tableName: string; - canRead: boolean; - }> = await Promise.all( - tableForeignKeys.map(async (foreignKey) => { - const cenTableRead = await this._dbContext.userAccessRepository.improvedCheckTableRead( - userId, - connectionId, - foreignKey.referenced_table_name, - masterPwd, - ); - return { - tableName: foreignKey.referenced_table_name, - canRead: cenTableRead, - }; - }), - ); - tableForeignKeys = tableForeignKeys.filter((foreignKey) => { - return canUserReadForeignTables.find((el) => { - return el.tableName === foreignKey.referenced_table_name && el.canRead; - }); - }); + let foreignKeysWithAutocompleteColumns: Array = []; + const canUserReadForeignTables: Array<{ + tableName: string; + canRead: boolean; + }> = await Promise.all( + tableForeignKeys.map(async (foreignKey) => { + const cenTableRead = await this._dbContext.userAccessRepository.improvedCheckTableRead( + userId, + connectionId, + foreignKey.referenced_table_name, + masterPwd, + ); + return { + tableName: foreignKey.referenced_table_name, + canRead: cenTableRead, + }; + }), + ); + tableForeignKeys = tableForeignKeys.filter((foreignKey) => { + return canUserReadForeignTables.find((el) => { + return el.tableName === foreignKey.referenced_table_name && el.canRead; + }); + }); - if (tableForeignKeys && tableForeignKeys.length > 0) { - foreignKeysWithAutocompleteColumns = await Promise.all( - tableForeignKeys.map((el) => { - try { - return this.attachForeignColumnNames(el, userId, connectionId, dao); - } catch (_e) { - return el as ForeignKeyWithAutocompleteColumnsDS; - } - }), - ); - } + if (tableForeignKeys && tableForeignKeys.length > 0) { + foreignKeysWithAutocompleteColumns = await Promise.all( + tableForeignKeys.map((el) => { + try { + return this.attachForeignColumnNames(el, userId, connectionId, dao); + } catch (_e) { + return el as ForeignKeyWithAutocompleteColumnsDS; + } + }), + ); + } - const availablePrimaryColumns = tablePrimaryKeys.map((key) => { - return key.column_name; - }); - for (const key in primaryKey) { - // eslint-disable-next-line security/detect-object-injection - if (!primaryKey[key] && primaryKey[key] !== '') delete primaryKey[key]; - } - const receivedPrimaryColumns = Object.keys(primaryKey); + const availablePrimaryColumns = tablePrimaryKeys.map((key) => { + return key.column_name; + }); + for (const key in primaryKey) { + // eslint-disable-next-line security/detect-object-injection + if (!primaryKey[key] && primaryKey[key] !== '') delete primaryKey[key]; + } + const receivedPrimaryColumns = Object.keys(primaryKey); - if (!compareArrayElements(availablePrimaryColumns, receivedPrimaryColumns)) { - throw new HttpException( - { - message: Messages.PRIMARY_KEY_INVALID, - }, - HttpStatus.BAD_REQUEST, - ); - } + if (!compareArrayElements(availablePrimaryColumns, receivedPrimaryColumns)) { + throw new HttpException( + { + message: Messages.PRIMARY_KEY_INVALID, + }, + HttpStatus.BAD_REQUEST, + ); + } - let oldRowData: Record; - try { - oldRowData = await dao.getRowByPrimaryKey(tableName, primaryKey, builtDAOsTableSettings, userEmail); - } catch (e) { - throw new UnknownSQLException(e.message, ExceptionOperations.FAILED_TO_UPDATE_ROW_IN_TABLE); - } - if (!oldRowData) { - throw new HttpException( - { - message: Messages.ROW_PRIMARY_KEY_NOT_FOUND, - }, - HttpStatus.BAD_REQUEST, - ); - } + let oldRowData: Record; + try { + oldRowData = await dao.getRowByPrimaryKey(tableName, primaryKey, builtDAOsTableSettings, userEmail); + } catch (e) { + throw new UnknownSQLException(e.message, ExceptionOperations.FAILED_TO_UPDATE_ROW_IN_TABLE); + } + if (!oldRowData) { + throw new HttpException( + { + message: Messages.ROW_PRIMARY_KEY_NOT_FOUND, + }, + HttpStatus.BAD_REQUEST, + ); + } - const oldRowDataLog = { - ...oldRowData, - }; + const oldRowDataLog = { + ...oldRowData, + }; - const futureRowData = Object.assign(oldRowData, row); - let futurePrimaryKey = {}; - for (const primaryColumn of tablePrimaryKeys) { - futurePrimaryKey[primaryColumn.column_name] = futureRowData[primaryColumn.column_name]; - } - if (isObjectEmpty(futurePrimaryKey)) { - futurePrimaryKey = primaryKey; - } + const futureRowData = Object.assign(oldRowData, row); + let futurePrimaryKey = {}; + for (const primaryColumn of tablePrimaryKeys) { + futurePrimaryKey[primaryColumn.column_name] = futureRowData[primaryColumn.column_name]; + } + if (isObjectEmpty(futurePrimaryKey)) { + futurePrimaryKey = primaryKey; + } - const formedTableStructure = formFullTableStructure(tableStructure, tableSettings); - try { - row = await hashPasswordsInRowUtil(row, tableWidgets); - row = processUuidsInRowUtil(row, tableWidgets); - await dao.updateRowInTable(tableName, row, primaryKey, userEmail); - operationResult = OperationResultStatusEnum.successfully; - let updatedRow = await dao.getRowByPrimaryKey(tableName, futurePrimaryKey, builtDAOsTableSettings, userEmail); - updatedRow = removePasswordsFromRowsUtil(updatedRow, tableWidgets); - updatedRow = convertBinaryDataInRowUtil(updatedRow, tableStructure); - return { - row: updatedRow, - foreignKeys: foreignKeysWithAutocompleteColumns, - primaryColumns: tablePrimaryKeys, - structure: formedTableStructure, - table_widgets: tableWidgets, - display_name: tableSettings?.display_name ? tableSettings.display_name : null, - readonly_fields: tableSettings?.readonly_fields ? tableSettings.readonly_fields : [], - list_fields: personalTableSettings?.list_fields?.length > 0 ? personalTableSettings.list_fields : [], - identity_column: tableSettings?.identity_column ? tableSettings.identity_column : null, - referenced_table_names_and_columns: referencedTableNamesAndColumnsWithTablesDisplayNames, - excluded_fields: tableSettings?.excluded_fields ? tableSettings.excluded_fields : [], - can_delete: tableSettings ? tableSettings.can_delete : true, - can_update: tableSettings ? tableSettings.can_update : true, - can_add: tableSettings ? tableSettings.can_add : true, - table_settings: { - sortable_by: tableSettings?.sortable_by?.length > 0 ? tableSettings.sortable_by : [], - ordering: personalTableSettings?.ordering ? personalTableSettings.ordering : undefined, - identity_column: tableSettings?.identity_column ? tableSettings.identity_column : null, - list_fields: personalTableSettings?.list_fields?.length > 0 ? personalTableSettings.list_fields : [], - allow_csv_export: tableSettings ? tableSettings.allow_csv_export : true, - allow_csv_import: tableSettings ? tableSettings.allow_csv_import : true, - can_delete: tableSettings ? tableSettings.can_delete : true, - can_update: tableSettings ? tableSettings.can_update : true, - can_add: tableSettings ? tableSettings.can_add : true, - }, - }; - } catch (e) { - operationResult = OperationResultStatusEnum.unsuccessfully; - throw new UnknownSQLException(e.message, ExceptionOperations.FAILED_TO_UPDATE_ROW_IN_TABLE); - } finally { - const logRecord = { - table_name: tableName, - userId: userId, - connection: connection, - operationType: LogOperationTypeEnum.updateRow, - operationStatusResult: operationResult, - row: row, - old_data: oldRowDataLog, - table_primary_key: primaryKey, - }; - await this.tableLogsService.crateAndSaveNewLogUtil(logRecord); - const isTest = isTestConnectionUtil(connection); - await this.amplitudeService.formAndSendLogRecord( - isTest ? AmplitudeEventTypeEnum.tableRowAddedTest : AmplitudeEventTypeEnum.tableRowAdded, - userId, - ); - const foundAddTableActions = await this._dbContext.tableActionRepository.findTableActionsWithUpdateRowEvents( - connectionId, - tableName, - ); - await this.tableActionActivationService.activateTableActions( - foundAddTableActions, - connection, - primaryKey, - userId, - tableName, - TableActionEventEnum.UPDATE_ROW, - ); - } - } + const formedTableStructure = formFullTableStructure(tableStructure, tableSettings); + try { + row = await hashPasswordsInRowUtil(row, tableWidgets); + row = processUuidsInRowUtil(row, tableWidgets); + await dao.updateRowInTable(tableName, row, primaryKey, userEmail); + operationResult = OperationResultStatusEnum.successfully; + let updatedRow = await dao.getRowByPrimaryKey(tableName, futurePrimaryKey, builtDAOsTableSettings, userEmail); + updatedRow = removePasswordsFromRowsUtil(updatedRow, tableWidgets); + updatedRow = convertBinaryDataInRowUtil(updatedRow, tableStructure); + return { + row: updatedRow, + foreignKeys: foreignKeysWithAutocompleteColumns, + primaryColumns: tablePrimaryKeys, + structure: formedTableStructure, + table_widgets: tableWidgets, + display_name: tableSettings?.display_name ? tableSettings.display_name : null, + readonly_fields: tableSettings?.readonly_fields ? tableSettings.readonly_fields : [], + list_fields: personalTableSettings?.list_fields?.length > 0 ? personalTableSettings.list_fields : [], + identity_column: tableSettings?.identity_column ? tableSettings.identity_column : null, + referenced_table_names_and_columns: referencedTableNamesAndColumnsWithTablesDisplayNames, + excluded_fields: tableSettings?.excluded_fields ? tableSettings.excluded_fields : [], + can_delete: tableSettings ? tableSettings.can_delete : true, + can_update: tableSettings ? tableSettings.can_update : true, + can_add: tableSettings ? tableSettings.can_add : true, + table_settings: { + sortable_by: tableSettings?.sortable_by?.length > 0 ? tableSettings.sortable_by : [], + ordering: personalTableSettings?.ordering ? personalTableSettings.ordering : undefined, + identity_column: tableSettings?.identity_column ? tableSettings.identity_column : null, + list_fields: personalTableSettings?.list_fields?.length > 0 ? personalTableSettings.list_fields : [], + allow_csv_export: tableSettings ? tableSettings.allow_csv_export : true, + allow_csv_import: tableSettings ? tableSettings.allow_csv_import : true, + can_delete: tableSettings ? tableSettings.can_delete : true, + can_update: tableSettings ? tableSettings.can_update : true, + can_add: tableSettings ? tableSettings.can_add : true, + ordering_field: personalTableSettings?.ordering_field ? personalTableSettings.ordering_field : undefined, + }, + }; + } catch (e) { + operationResult = OperationResultStatusEnum.unsuccessfully; + throw new UnknownSQLException(e.message, ExceptionOperations.FAILED_TO_UPDATE_ROW_IN_TABLE); + } finally { + const logRecord = { + table_name: tableName, + userId: userId, + connection: connection, + operationType: LogOperationTypeEnum.updateRow, + operationStatusResult: operationResult, + row: row, + old_data: oldRowDataLog, + table_primary_key: primaryKey, + }; + await this.tableLogsService.crateAndSaveNewLogUtil(logRecord); + const isTest = isTestConnectionUtil(connection); + await this.amplitudeService.formAndSendLogRecord( + isTest ? AmplitudeEventTypeEnum.tableRowAddedTest : AmplitudeEventTypeEnum.tableRowAdded, + userId, + ); + const foundAddTableActions = await this._dbContext.tableActionRepository.findTableActionsWithUpdateRowEvents( + connectionId, + tableName, + ); + await this.tableActionActivationService.activateTableActions( + foundAddTableActions, + connection, + primaryKey, + userId, + tableName, + TableActionEventEnum.UPDATE_ROW, + ); + } + } - private async attachForeignColumnNames( - foreignKey: ForeignKeyDS, - userId: string, - connectionId: string, - dao: IDataAccessObject | IDataAccessObjectAgent, - ): Promise { - try { - const [foreignTableSettings, foreignTableStructure] = await Promise.all([ - this._dbContext.tableSettingsRepository.findTableSettings(connectionId, foreignKey.referenced_table_name), - dao.getTableStructure(foreignKey.referenced_table_name, userId), - ]); + private async attachForeignColumnNames( + foreignKey: ForeignKeyDS, + userId: string, + connectionId: string, + dao: IDataAccessObject | IDataAccessObjectAgent, + ): Promise { + try { + const [foreignTableSettings, foreignTableStructure] = await Promise.all([ + this._dbContext.tableSettingsRepository.findTableSettings(connectionId, foreignKey.referenced_table_name), + dao.getTableStructure(foreignKey.referenced_table_name, userId), + ]); - let columnNames = foreignTableStructure.map((el) => { - return el.column_name; - }); - if (foreignTableSettings && foreignTableSettings.autocomplete_columns.length > 0) { - columnNames = columnNames.filter((el) => { - const index = foreignTableSettings.autocomplete_columns.indexOf(el); - return index >= 0; - }); - } - return { - ...foreignKey, - autocomplete_columns: columnNames, - }; - } catch (_e) { - return { - ...foreignKey, - autocomplete_columns: [], - }; - } - } + let columnNames = foreignTableStructure.map((el) => { + return el.column_name; + }); + if (foreignTableSettings && foreignTableSettings.autocomplete_columns.length > 0) { + columnNames = columnNames.filter((el) => { + const index = foreignTableSettings.autocomplete_columns.indexOf(el); + return index >= 0; + }); + } + return { + ...foreignKey, + autocomplete_columns: columnNames, + }; + } catch (_e) { + return { + ...foreignKey, + autocomplete_columns: [], + }; + } + } }