diff --git a/backend/src/entities/table-settings/application/data-structures/create-table-settings.ds.ts b/backend/src/entities/table-settings/application/data-structures/create-table-settings.ds.ts index 6b1facdb6..11d435d58 100644 --- a/backend/src/entities/table-settings/application/data-structures/create-table-settings.ds.ts +++ b/backend/src/entities/table-settings/application/data-structures/create-table-settings.ds.ts @@ -65,4 +65,16 @@ export class CreateTableSettingsDs { @ApiProperty() allow_csv_import: boolean; + + @ApiProperty({ required: false}) + list_per_page?: number; + + @ApiProperty({ isArray: true, type: 'string', required: false }) + list_fields?: Array; + + @ApiProperty({ required: false }) + ordering?: string; + + @ApiProperty({ required: false }) + ordering_field?: string; } diff --git a/backend/src/entities/table-settings/application/data-structures/found-table-settings.ds.ts b/backend/src/entities/table-settings/application/data-structures/found-table-settings.ds.ts index 38912f375..5809d8b36 100644 --- a/backend/src/entities/table-settings/application/data-structures/found-table-settings.ds.ts +++ b/backend/src/entities/table-settings/application/data-structures/found-table-settings.ds.ts @@ -4,69 +4,81 @@ import { TableActionEntity } from '../../../table-actions/table-actions-module/t import { TableWidgetEntity } from '../../../widget/table-widget.entity.js'; export class FoundTableSettingsDs { - @ApiProperty() - id: string; + @ApiProperty() + id: string; - @ApiProperty() - table_name: string; + @ApiProperty() + table_name: string; - @ApiProperty() - display_name: string; + @ApiProperty() + display_name: string; - @ApiProperty({ isArray: true, type: String }) - search_fields: Array; + @ApiProperty({ isArray: true, type: String }) + search_fields: Array; - @ApiProperty({ isArray: true, type: String }) - excluded_fields: Array; + @ApiProperty({ isArray: true, type: String }) + excluded_fields: Array; - @ApiProperty({ isArray: true, type: String }) - identification_fields: Array; + @ApiProperty({ isArray: true, type: String }) + identification_fields: Array; - @ApiProperty() - identity_column: string; + @ApiProperty() + identity_column: string; - @ApiProperty({ isArray: true, type: String }) - readonly_fields: Array; + @ApiProperty({ isArray: true, type: String }) + readonly_fields: Array; - @ApiProperty({ isArray: true, type: String }) - sensitive_fields: Array; + @ApiProperty({ isArray: true, type: String }) + sensitive_fields: Array; - @ApiProperty({ isArray: true, type: String }) - sortable_by: Array; + @ApiProperty({ isArray: true, type: String }) + sortable_by: Array; - @ApiProperty({ isArray: true, type: String }) - autocomplete_columns: Array; + @ApiProperty({ isArray: true, type: String }) + autocomplete_columns: Array; - @ApiProperty({ isArray: true, type: String }) - columns_view: Array; + @ApiProperty({ isArray: true, type: String }) + columns_view: Array; - @ApiProperty() - connection_id: string; + @ApiProperty() + connection_id: string; - @ApiProperty({ isArray: true, type: CustomFieldsEntity }) - custom_fields: Array; + @ApiProperty({ isArray: true, type: CustomFieldsEntity }) + custom_fields: Array; - @ApiProperty({ isArray: true, type: TableWidgetEntity }) - table_widgets: Array; + @ApiProperty({ isArray: true, type: TableWidgetEntity }) + table_widgets: Array; - @ApiProperty({ isArray: true, type: TableActionEntity }) - table_actions: Array; + @ApiProperty({ isArray: true, type: TableActionEntity }) + table_actions: Array; - @ApiProperty() - can_add: boolean; + @ApiProperty() + can_add: boolean; - @ApiProperty() - can_delete: boolean; + @ApiProperty() + can_delete: boolean; - @ApiProperty() - can_update: boolean; + @ApiProperty() + can_update: boolean; - @ApiProperty() - icon: string; + @ApiProperty() + icon: string; - @ApiProperty() - allow_csv_export: boolean; + @ApiProperty() + allow_csv_export: boolean; - @ApiProperty() - allow_csv_import: boolean; + @ApiProperty() + allow_csv_import: boolean; + + @ApiProperty({ isArray: true, type: 'string' }) + list_fields: string[]; + + @ApiProperty() + list_per_page: number; + + @ApiProperty() + ordering: string; + + @ApiProperty() + ordering_field: string; } diff --git a/backend/src/entities/table-settings/common-table-settings/table-settings.controller.ts b/backend/src/entities/table-settings/common-table-settings/table-settings.controller.ts index 57b5f1de3..f10b5cdec 100644 --- a/backend/src/entities/table-settings/common-table-settings/table-settings.controller.ts +++ b/backend/src/entities/table-settings/common-table-settings/table-settings.controller.ts @@ -1,16 +1,16 @@ import { - Body, - ClassSerializerInterceptor, - Controller, - Delete, - Get, - HttpStatus, - Inject, - Injectable, - Post, - Put, - UseGuards, - UseInterceptors, + Body, + ClassSerializerInterceptor, + Controller, + Delete, + Get, + HttpStatus, + Inject, + Injectable, + Post, + Put, + UseGuards, + UseInterceptors, } from '@nestjs/common'; import { HttpException } from '@nestjs/common/exceptions/http.exception.js'; import { ApiBearerAuth, ApiBody, ApiOperation, ApiQuery, ApiResponse, ApiTags } from '@nestjs/swagger'; @@ -28,10 +28,10 @@ import { FindTableSettingsDs } from '../application/data-structures/find-table-s import { FoundTableSettingsDs } from '../application/data-structures/found-table-settings.ds.js'; import { CreateTableSettingsDto } from './dto/index.js'; import { - ICreateTableSettings, - IDeleteTableSettings, - IFindTableSettings, - IUpdateTableSettings, + ICreateTableSettings, + IDeleteTableSettings, + IFindTableSettings, + IUpdateTableSettings, } from './use-cases/use-cases.interface.js'; @UseInterceptors(SentryInterceptor) @@ -40,225 +40,233 @@ import { @ApiTags('Table settings') @Injectable() export class TableSettingsController { - constructor( - @Inject(UseCaseType.FIND_TABLE_SETTINGS) - private readonly findTableSettingsUseCase: IFindTableSettings, - @Inject(UseCaseType.CREATE_TABLE_SETTINGS) - private readonly createTableSettingsUseCase: ICreateTableSettings, - @Inject(UseCaseType.UPDATE_TABLE_SETTINGS) - private readonly updateTableSettingsUseCase: IUpdateTableSettings, - @Inject(UseCaseType.DELETE_TABLE_SETTINGS) - private readonly deleteTableSettingsUseCase: IDeleteTableSettings, - ) {} + constructor( + @Inject(UseCaseType.FIND_TABLE_SETTINGS) + private readonly findTableSettingsUseCase: IFindTableSettings, + @Inject(UseCaseType.CREATE_TABLE_SETTINGS) + private readonly createTableSettingsUseCase: ICreateTableSettings, + @Inject(UseCaseType.UPDATE_TABLE_SETTINGS) + private readonly updateTableSettingsUseCase: IUpdateTableSettings, + @Inject(UseCaseType.DELETE_TABLE_SETTINGS) + private readonly deleteTableSettingsUseCase: IDeleteTableSettings, + ) {} - @ApiOperation({ summary: 'Find all table settings in this connection' }) - @ApiResponse({ - status: 200, - description: 'Table settings found.', - type: FoundTableSettingsDs, - }) - @ApiQuery({ name: 'connectionId', required: true }) - @ApiQuery({ name: 'tableName', required: true }) - @UseGuards(ConnectionReadGuard) - @Get('/settings/') - @UseInterceptors(ClassSerializerInterceptor) - async findAll( - @QueryUuid('connectionId') connectionId: string, - @QueryTableName() tableName: string, - @MasterPassword() masterPwd: string, - ): Promise { - if (!connectionId) { - throw new HttpException( - { - message: Messages.CONNECTION_ID_MISSING, - }, - HttpStatus.BAD_REQUEST, - ); - } + @ApiOperation({ summary: 'Find all table settings in this connection' }) + @ApiResponse({ + status: 200, + description: 'Table settings found.', + type: FoundTableSettingsDs, + }) + @ApiQuery({ name: 'connectionId', required: true }) + @ApiQuery({ name: 'tableName', required: true }) + @UseGuards(ConnectionReadGuard) + @Get('/settings/') + @UseInterceptors(ClassSerializerInterceptor) + async findAll( + @QueryUuid('connectionId') connectionId: string, + @QueryTableName() tableName: string, + @MasterPassword() masterPwd: string, + ): Promise { + if (!connectionId) { + throw new HttpException( + { + message: Messages.CONNECTION_ID_MISSING, + }, + HttpStatus.BAD_REQUEST, + ); + } - const inputData: FindTableSettingsDs = { - connectionId: connectionId, - tableName: tableName, - masterPassword: masterPwd, - }; - return await this.findTableSettingsUseCase.execute(inputData, InTransactionEnum.OFF); - } + const inputData: FindTableSettingsDs = { + connectionId: connectionId, + tableName: tableName, + masterPassword: masterPwd, + }; + return await this.findTableSettingsUseCase.execute(inputData, InTransactionEnum.OFF); + } - @ApiOperation({ summary: 'Create new table settings' }) - @ApiBody({ type: CreateTableSettingsDs }) - @ApiResponse({ - status: 201, - description: 'Table settings created.', - type: FoundTableSettingsDs, - }) - @ApiQuery({ name: 'connectionId', required: true }) - @ApiQuery({ name: 'tableName', required: true }) - @UseGuards(ConnectionEditGuard) - @Post('/settings/') - async createSettings( - @QueryUuid('connectionId') connectionId: string, - @QueryTableName() tableName: string, - @Body('search_fields') search_fields: Array, - @Body('display_name') display_name: string, - @Body('excluded_fields') excluded_fields: Array, - @Body('identification_fields') identification_fields: Array, - @Body('readonly_fields') readonly_fields: Array, - @Body('sensitive_fields') sensitive_fields: Array, - @Body('sortable_by') sortable_by: Array, - @Body('autocomplete_columns') autocomplete_columns: Array, - @Body('customFields') customFields: Array, - @Body('identity_column') identity_column: string, - @Body('can_delete') can_delete: boolean, - @Body('can_update') can_update: boolean, - @Body('can_add') can_add: boolean, - @Body('icon') icon: string, - @Body('allow_csv_export') allow_csv_export: boolean, - @Body('allow_csv_import') allow_csv_import: boolean, - @UserId() userId: string, - @MasterPassword() masterPwd: string, - ): Promise { - const inputData: CreateTableSettingsDs = { - table_name: tableName, - display_name: display_name, - connection_id: connectionId, - search_fields: search_fields, - excluded_fields: excluded_fields, - readonly_fields: readonly_fields, - sortable_by: sortable_by, - autocomplete_columns: autocomplete_columns, - custom_fields: customFields, - identification_fields: identification_fields, - sensitive_fields: sensitive_fields, - identity_column: identity_column, - masterPwd: masterPwd, - userId: userId, - table_widgets: undefined, - can_delete: can_delete, - can_update: can_update, - can_add: can_add, - icon: icon, - allow_csv_export: allow_csv_export, - allow_csv_import: allow_csv_import, - }; + @ApiOperation({ summary: 'Create new table settings' }) + @ApiBody({ type: CreateTableSettingsDs }) + @ApiResponse({ + status: 201, + description: 'Table settings created.', + type: FoundTableSettingsDs, + }) + @ApiQuery({ name: 'connectionId', required: true }) + @ApiQuery({ name: 'tableName', required: true }) + @UseGuards(ConnectionEditGuard) + @Post('/settings/') + async createSettings( + @QueryUuid('connectionId') connectionId: string, + @QueryTableName() tableName: string, + @Body('search_fields') search_fields: Array, + @Body('display_name') display_name: string, + @Body('excluded_fields') excluded_fields: Array, + @Body('identification_fields') identification_fields: Array, + @Body('readonly_fields') readonly_fields: Array, + @Body('sensitive_fields') sensitive_fields: Array, + @Body('sortable_by') sortable_by: Array, + @Body('autocomplete_columns') autocomplete_columns: Array, + @Body('customFields') customFields: Array, + @Body('identity_column') identity_column: string, + @Body('can_delete') can_delete: boolean, + @Body('can_update') can_update: boolean, + @Body('can_add') can_add: boolean, + @Body('icon') icon: string, + @Body('allow_csv_export') allow_csv_export: boolean, + @Body('allow_csv_import') allow_csv_import: boolean, + @Body('list_per_page') list_per_page: number, + @Body('list_fields') list_fields: string[], + @Body('ordering') ordering: string, + @Body('ordering_field') ordering_field: string, + @UserId() userId: string, + @MasterPassword() masterPwd: string, + ): Promise { + const inputData: CreateTableSettingsDs = { + table_name: tableName, + display_name: display_name, + connection_id: connectionId, + search_fields: search_fields, + excluded_fields: excluded_fields, + readonly_fields: readonly_fields, + sortable_by: sortable_by, + autocomplete_columns: autocomplete_columns, + custom_fields: customFields, + identification_fields: identification_fields, + sensitive_fields: sensitive_fields, + identity_column: identity_column, + masterPwd: masterPwd, + userId: userId, + table_widgets: undefined, + can_delete: can_delete, + can_update: can_update, + can_add: can_add, + icon: icon, + allow_csv_export: allow_csv_export, + allow_csv_import: allow_csv_import, + list_per_page: list_per_page, + list_fields: list_fields, + ordering: ordering, + ordering_field: ordering_field, + }; - const errors = this.validateParameters(inputData); - if (errors.length > 0) { - throw new HttpException( - { - message: toPrettyErrorsMsg(errors), - }, - HttpStatus.BAD_REQUEST, - ); - } - return await this.createTableSettingsUseCase.execute(inputData, InTransactionEnum.OFF); - } + const errors = this.validateParameters(inputData); + if (errors.length > 0) { + throw new HttpException( + { + message: toPrettyErrorsMsg(errors), + }, + HttpStatus.BAD_REQUEST, + ); + } + return await this.createTableSettingsUseCase.execute(inputData, InTransactionEnum.OFF); + } - @ApiOperation({ summary: 'Update table settings' }) - @ApiBody({ type: CreateTableSettingsDs }) - @ApiResponse({ - status: 200, - description: 'Table settings updated.', - type: FoundTableSettingsDs, - }) - @ApiQuery({ name: 'connectionId', required: true }) - @ApiQuery({ name: 'tableName', required: true }) - @UseGuards(ConnectionEditGuard) - @Put('/settings/') - async updateSettings( - @QueryUuid('connectionId') connectionId: string, - @QueryTableName() tableName: string, - @Body('search_fields') search_fields: Array, - @Body('display_name') display_name: string, - @Body('excluded_fields') excluded_fields: Array, - @Body('identification_fields') identification_fields: Array, - @Body('readonly_fields') readonly_fields: Array, - @Body('sensitive_fields') sensitive_fields: Array, - @Body('sortable_by') sortable_by: Array, - @Body('autocomplete_columns') autocomplete_columns: Array, - @Body('customFields') customFields: Array, - @Body('identity_column') identity_column: string, - @Body('can_delete') can_delete: boolean, - @Body('can_update') can_update: boolean, - @Body('can_add') can_add: boolean, - @Body('icon') icon: string, - @Body('allow_csv_export') allow_csv_export: boolean, - @Body('allow_csv_import') allow_csv_import: boolean, - @UserId() userId: string, - @MasterPassword() masterPwd: string, - ): Promise { - const inputData: CreateTableSettingsDs = { - autocomplete_columns: autocomplete_columns, - connection_id: connectionId, - custom_fields: customFields, - display_name: display_name, - excluded_fields: excluded_fields, - identification_fields: identification_fields, - identity_column: identity_column, - masterPwd: masterPwd, - readonly_fields: readonly_fields, - search_fields: search_fields, - sensitive_fields: sensitive_fields, - sortable_by: sortable_by, - table_name: tableName, - userId: userId, - can_delete: can_delete, - can_update: can_update, - can_add: can_add, - icon: icon, - allow_csv_export: allow_csv_export, - allow_csv_import: allow_csv_import, - }; + @ApiOperation({ summary: 'Update table settings' }) + @ApiBody({ type: CreateTableSettingsDs }) + @ApiResponse({ + status: 200, + description: 'Table settings updated.', + type: FoundTableSettingsDs, + }) + @ApiQuery({ name: 'connectionId', required: true }) + @ApiQuery({ name: 'tableName', required: true }) + @UseGuards(ConnectionEditGuard) + @Put('/settings/') + async updateSettings( + @QueryUuid('connectionId') connectionId: string, + @QueryTableName() tableName: string, + @Body('search_fields') search_fields: Array, + @Body('display_name') display_name: string, + @Body('excluded_fields') excluded_fields: Array, + @Body('identification_fields') identification_fields: Array, + @Body('readonly_fields') readonly_fields: Array, + @Body('sensitive_fields') sensitive_fields: Array, + @Body('sortable_by') sortable_by: Array, + @Body('autocomplete_columns') autocomplete_columns: Array, + @Body('customFields') customFields: Array, + @Body('identity_column') identity_column: string, + @Body('can_delete') can_delete: boolean, + @Body('can_update') can_update: boolean, + @Body('can_add') can_add: boolean, + @Body('icon') icon: string, + @Body('allow_csv_export') allow_csv_export: boolean, + @Body('allow_csv_import') allow_csv_import: boolean, + @UserId() userId: string, + @MasterPassword() masterPwd: string, + ): Promise { + const inputData: CreateTableSettingsDs = { + autocomplete_columns: autocomplete_columns, + connection_id: connectionId, + custom_fields: customFields, + display_name: display_name, + excluded_fields: excluded_fields, + identification_fields: identification_fields, + identity_column: identity_column, + masterPwd: masterPwd, + readonly_fields: readonly_fields, + search_fields: search_fields, + sensitive_fields: sensitive_fields, + sortable_by: sortable_by, + table_name: tableName, + userId: userId, + can_delete: can_delete, + can_update: can_update, + can_add: can_add, + icon: icon, + allow_csv_export: allow_csv_export, + allow_csv_import: allow_csv_import, + }; - const errors = this.validateParameters(inputData); - if (errors.length > 0) { - throw new HttpException( - { - message: toPrettyErrorsMsg(errors), - }, - HttpStatus.BAD_REQUEST, - ); - } - return await this.updateTableSettingsUseCase.execute(inputData, InTransactionEnum.ON); - } + const errors = this.validateParameters(inputData); + if (errors.length > 0) { + throw new HttpException( + { + message: toPrettyErrorsMsg(errors), + }, + HttpStatus.BAD_REQUEST, + ); + } + return await this.updateTableSettingsUseCase.execute(inputData, InTransactionEnum.ON); + } - @ApiOperation({ summary: 'Delete table settings' }) - @ApiBody({ type: CreateTableSettingsDs }) - @ApiResponse({ - status: 200, - description: 'Table settings deleted.', - type: FoundTableSettingsDs, - }) - @ApiQuery({ name: 'connectionId', required: true }) - @ApiQuery({ name: 'tableName', required: true }) - @UseGuards(ConnectionEditGuard) - @Delete('/settings/') - async deleteSettings( - @QueryUuid('connectionId') connectionId: string, - @QueryTableName() tableName: string, - ): Promise { - if (!connectionId) { - throw new HttpException( - { - message: Messages.PARAMETER_MISSING, - }, - HttpStatus.BAD_REQUEST, - ); - } - const inputData: DeleteTableSettingsDs = { - connectionId: connectionId, - tableName: tableName, - }; - return await this.deleteTableSettingsUseCase.execute(inputData, InTransactionEnum.ON); - } + @ApiOperation({ summary: 'Delete table settings' }) + @ApiBody({ type: CreateTableSettingsDs }) + @ApiResponse({ + status: 200, + description: 'Table settings deleted.', + type: FoundTableSettingsDs, + }) + @ApiQuery({ name: 'connectionId', required: true }) + @ApiQuery({ name: 'tableName', required: true }) + @UseGuards(ConnectionEditGuard) + @Delete('/settings/') + async deleteSettings( + @QueryUuid('connectionId') connectionId: string, + @QueryTableName() tableName: string, + ): Promise { + if (!connectionId) { + throw new HttpException( + { + message: Messages.PARAMETER_MISSING, + }, + HttpStatus.BAD_REQUEST, + ); + } + const inputData: DeleteTableSettingsDs = { + connectionId: connectionId, + tableName: tableName, + }; + return await this.deleteTableSettingsUseCase.execute(inputData, InTransactionEnum.ON); + } - private validateParameters(tableSettingsDTO: CreateTableSettingsDto): Array { - const errors = []; - if (!tableSettingsDTO.table_name) { - errors.push(Messages.TABLE_NAME_MISSING); - } - if (!tableSettingsDTO.connection_id) { - errors.push(Messages.CONNECTION_ID_MISSING); - } - return errors; - } + private validateParameters(tableSettingsDTO: CreateTableSettingsDto): Array { + const errors = []; + if (!tableSettingsDTO.table_name) { + errors.push(Messages.TABLE_NAME_MISSING); + } + if (!tableSettingsDTO.connection_id) { + errors.push(Messages.CONNECTION_ID_MISSING); + } + return errors; + } } diff --git a/backend/src/entities/table-settings/common-table-settings/utils/build-empty-table-settings.ts b/backend/src/entities/table-settings/common-table-settings/utils/build-empty-table-settings.ts index ef6576894..f9e5c2b1c 100644 --- a/backend/src/entities/table-settings/common-table-settings/utils/build-empty-table-settings.ts +++ b/backend/src/entities/table-settings/common-table-settings/utils/build-empty-table-settings.ts @@ -1,59 +1,67 @@ import { CreateTableSettingsDs } from '../../application/data-structures/create-table-settings.ds.js'; export function buildEmptyTableSettings(connectionId: string, tableName: string): CreateTableSettingsDs { - return { - autocomplete_columns: undefined, - connection_id: connectionId, - custom_fields: undefined, - display_name: undefined, - excluded_fields: undefined, - identification_fields: undefined, - identity_column: undefined, - masterPwd: undefined, - readonly_fields: undefined, - search_fields: undefined, - sensitive_fields: undefined, - sortable_by: undefined, - table_name: tableName, - table_widgets: undefined, - table_actions: undefined, - userId: undefined, - can_add: undefined, - can_delete: undefined, - can_update: undefined, - icon: undefined, - allow_csv_export: true, - allow_csv_import: true, - }; + return { + autocomplete_columns: undefined, + connection_id: connectionId, + custom_fields: undefined, + display_name: undefined, + excluded_fields: undefined, + identification_fields: undefined, + identity_column: undefined, + masterPwd: undefined, + readonly_fields: undefined, + search_fields: undefined, + sensitive_fields: undefined, + sortable_by: undefined, + table_name: tableName, + table_widgets: undefined, + table_actions: undefined, + userId: undefined, + can_add: undefined, + can_delete: undefined, + can_update: undefined, + icon: undefined, + allow_csv_export: true, + allow_csv_import: true, + list_fields: undefined, + list_per_page: undefined, + ordering: undefined, + ordering_field: undefined, + }; } export function buildEmptyTableSettingsWithEmptyWidgets( - connectionId: string, - tableName: string, - userId: string, + connectionId: string, + tableName: string, + userId: string, ): CreateTableSettingsDs { - return { - autocomplete_columns: undefined, - connection_id: connectionId, - custom_fields: undefined, - display_name: undefined, - excluded_fields: undefined, - identification_fields: undefined, - identity_column: undefined, - masterPwd: undefined, - readonly_fields: undefined, - search_fields: undefined, - sensitive_fields: undefined, - sortable_by: undefined, - table_name: tableName, - table_widgets: [], - table_actions: [], - userId: userId, - can_add: undefined, - can_delete: undefined, - can_update: undefined, - icon: undefined, - allow_csv_export: true, - allow_csv_import: true, - }; + return { + autocomplete_columns: undefined, + connection_id: connectionId, + custom_fields: undefined, + display_name: undefined, + excluded_fields: undefined, + identification_fields: undefined, + identity_column: undefined, + masterPwd: undefined, + readonly_fields: undefined, + search_fields: undefined, + sensitive_fields: undefined, + sortable_by: undefined, + table_name: tableName, + table_widgets: [], + table_actions: [], + userId: userId, + can_add: undefined, + can_delete: undefined, + can_update: undefined, + icon: undefined, + allow_csv_export: true, + allow_csv_import: true, + list_fields: undefined, + list_per_page: undefined, + ordering: undefined, + ordering_field: undefined, + }; } diff --git a/backend/src/entities/table-settings/common-table-settings/utils/build-found-table-settings-ds.ts b/backend/src/entities/table-settings/common-table-settings/utils/build-found-table-settings-ds.ts index 7345a9c19..fb9d916b4 100644 --- a/backend/src/entities/table-settings/common-table-settings/utils/build-found-table-settings-ds.ts +++ b/backend/src/entities/table-settings/common-table-settings/utils/build-found-table-settings-ds.ts @@ -3,55 +3,63 @@ import { FoundTableSettingsDs } from '../../application/data-structures/found-ta import { TableSettingsEntity } from '../table-settings.entity.js'; export function buildFoundTableSettingsDs(tableSettings: TableSettingsEntity): FoundTableSettingsDs { - const { - id, - table_name, - display_name, - search_fields, - excluded_fields, - identification_fields, - identity_column, - readonly_fields, - sensitive_fields, - sortable_by, - autocomplete_columns, - columns_view, - custom_fields, - table_widgets, - table_actions, - can_add, - can_delete, - can_update, - icon, - allow_csv_export, - allow_csv_import, - } = tableSettings; - let connection_id = tableSettings.connection_id as unknown; - if (connection_id instanceof ConnectionEntity) { - connection_id = connection_id.id; - } - return { - id: id, - table_name: table_name, - display_name: display_name, - search_fields: search_fields, - excluded_fields: excluded_fields, - identification_fields: identification_fields, - identity_column: identity_column, - readonly_fields: readonly_fields, - sensitive_fields: sensitive_fields, - sortable_by: sortable_by, - autocomplete_columns: autocomplete_columns, - columns_view: columns_view, - connection_id: connection_id as unknown as string, - custom_fields: custom_fields, - table_widgets: table_widgets, - table_actions: table_actions, - can_add: can_add, - can_delete: can_delete, - can_update: can_update, - icon: icon, - allow_csv_export: allow_csv_export, - allow_csv_import: allow_csv_import, - }; + const { + id, + table_name, + display_name, + search_fields, + excluded_fields, + identification_fields, + identity_column, + readonly_fields, + sensitive_fields, + sortable_by, + autocomplete_columns, + columns_view, + custom_fields, + table_widgets, + table_actions, + can_add, + can_delete, + can_update, + icon, + allow_csv_export, + allow_csv_import, + list_fields, + list_per_page, + ordering, + ordering_field, + } = tableSettings; + let connection_id = tableSettings.connection_id as unknown; + if (connection_id instanceof ConnectionEntity) { + connection_id = connection_id.id; + } + return { + id: id, + table_name: table_name, + display_name: display_name, + search_fields: search_fields, + excluded_fields: excluded_fields, + identification_fields: identification_fields, + identity_column: identity_column, + readonly_fields: readonly_fields, + sensitive_fields: sensitive_fields, + sortable_by: sortable_by, + autocomplete_columns: autocomplete_columns, + columns_view: columns_view, + connection_id: connection_id as unknown as string, + custom_fields: custom_fields, + table_widgets: table_widgets, + table_actions: table_actions, + can_add: can_add, + can_delete: can_delete, + can_update: can_update, + icon: icon, + allow_csv_export: allow_csv_export, + allow_csv_import: allow_csv_import, + list_fields, + list_per_page, + ordering, + ordering_field, + }; } diff --git a/backend/src/entities/table-settings/common-table-settings/utils/build-new-table-settings-entity.ts b/backend/src/entities/table-settings/common-table-settings/utils/build-new-table-settings-entity.ts index a8ea49f3f..219e5e9e2 100644 --- a/backend/src/entities/table-settings/common-table-settings/utils/build-new-table-settings-entity.ts +++ b/backend/src/entities/table-settings/common-table-settings/utils/build-new-table-settings-entity.ts @@ -1,52 +1,61 @@ import { ConnectionEntity } from '../../../connection/connection.entity.js'; import { CreateTableSettingsDs } from '../../application/data-structures/create-table-settings.ds.js'; import { TableSettingsEntity } from '../table-settings.entity.js'; +import { QueryOrderingEnum } from '../../../../enums/query-ordering.enum.js'; export function buildNewTableSettingsEntity( - settings: CreateTableSettingsDs, - connection: ConnectionEntity, + settings: CreateTableSettingsDs, + connection: ConnectionEntity, ): TableSettingsEntity { - const newSettings = new TableSettingsEntity(); - const { - autocomplete_columns, - custom_fields, - display_name, - excluded_fields, - identification_fields, - identity_column, - readonly_fields, - search_fields, - sortable_by, - table_name, - table_widgets, - table_actions, - sensitive_fields, - can_add, - can_delete, - can_update, - icon, - allow_csv_export, - allow_csv_import, - } = settings; - newSettings.connection_id = connection; - newSettings.display_name = display_name; - newSettings.table_name = table_name; - newSettings.search_fields = search_fields; - newSettings.excluded_fields = excluded_fields; - newSettings.readonly_fields = readonly_fields; - newSettings.sortable_by = sortable_by; - newSettings.autocomplete_columns = autocomplete_columns; - newSettings.custom_fields = custom_fields; - newSettings.table_widgets = table_widgets; - newSettings.identification_fields = identification_fields; - newSettings.sensitive_fields = sensitive_fields; - newSettings.identity_column = identity_column; - newSettings.table_actions = table_actions; - newSettings.can_add = can_add; - newSettings.can_delete = can_delete; - newSettings.can_update = can_update; - newSettings.icon = icon; - newSettings.allow_csv_export = allow_csv_export; - newSettings.allow_csv_import = allow_csv_import; - return newSettings; + const newSettings = new TableSettingsEntity(); + const { + autocomplete_columns, + custom_fields, + display_name, + excluded_fields, + identification_fields, + identity_column, + readonly_fields, + search_fields, + sortable_by, + table_name, + table_widgets, + table_actions, + sensitive_fields, + can_add, + can_delete, + can_update, + icon, + allow_csv_export, + allow_csv_import, + list_per_page, + list_fields, + ordering, + ordering_field, + } = settings; + newSettings.connection_id = connection; + newSettings.display_name = display_name; + newSettings.table_name = table_name; + newSettings.search_fields = search_fields; + newSettings.excluded_fields = excluded_fields; + newSettings.readonly_fields = readonly_fields; + newSettings.sortable_by = sortable_by; + newSettings.autocomplete_columns = autocomplete_columns; + newSettings.custom_fields = custom_fields; + newSettings.table_widgets = table_widgets; + newSettings.identification_fields = identification_fields; + newSettings.sensitive_fields = sensitive_fields; + newSettings.identity_column = identity_column; + newSettings.table_actions = table_actions; + newSettings.can_add = can_add; + newSettings.can_delete = can_delete; + newSettings.can_update = can_update; + newSettings.icon = icon; + newSettings.allow_csv_export = allow_csv_export; + newSettings.allow_csv_import = allow_csv_import; + newSettings.list_per_page = list_per_page; + newSettings.list_fields = list_fields; + newSettings.ordering = ordering as QueryOrderingEnum; + newSettings.ordering_field = ordering_field; + return newSettings; } 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 c3200f5fa..be0745da3 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 @@ -39,6 +39,9 @@ export class TableSettingsInRowsDS { @ApiProperty() can_add: boolean; + + @ApiProperty() + columns_view: string[]; } export class FoundTableRowsDs { @ApiProperty({ isArray: true }) 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 7a8407531..2b98b5eaf 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 @@ -213,6 +213,11 @@ export class AddRowInTableUseCase extends AbstractUseCase; if (addedRowPrimaryKey && !isObjectEmpty(addedRowPrimaryKey)) { operationResult = OperationResultStatusEnum.successfully; @@ -235,16 +240,17 @@ export class AddRowInTableUseCase extends AbstractUseCase 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, + sortable_by: builtDAOsTableSettings?.sortable_by?.length > 0 ? builtDAOsTableSettings.sortable_by : [], + ordering: builtDAOsTableSettings.ordering ? builtDAOsTableSettings.ordering : undefined, + identity_column: builtDAOsTableSettings.identity_column ? builtDAOsTableSettings.identity_column : null, + list_fields: builtDAOsTableSettings?.list_fields?.length > 0 ? builtDAOsTableSettings.list_fields : [], + allow_csv_export: allowCsvExport, + allow_csv_import: allowCsvImport, + can_delete: can_delete, + can_update: can_update, + can_add: can_add, + columns_view: builtDAOsTableSettings?.columns_view ? builtDAOsTableSettings.columns_view : [], + ordering_field: builtDAOsTableSettings.ordering_field ? builtDAOsTableSettings.ordering_field : undefined, }, }; } 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 bf3a878f0..f8a4d26ce 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 @@ -213,6 +213,11 @@ export class GetRowByPrimaryKeyUseCase return responseObject; }), ); + const allowCsvExport = tableSettings?.allow_csv_export ?? true; + const allowCsvImport = tableSettings?.allow_csv_import ?? true; + const can_delete = tableSettings?.can_delete ?? true; + const can_update = tableSettings?.can_update ?? true; + const can_add = tableSettings?.can_add ?? true; //todo remove unnecessary fields return { row: rowData, @@ -233,16 +238,17 @@ export class GetRowByPrimaryKeyUseCase 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, + sortable_by: builtDAOsTableSettings?.sortable_by?.length > 0 ? builtDAOsTableSettings.sortable_by : [], + ordering: builtDAOsTableSettings.ordering ? builtDAOsTableSettings.ordering : undefined, + identity_column: builtDAOsTableSettings.identity_column ? builtDAOsTableSettings.identity_column : null, + list_fields: builtDAOsTableSettings?.list_fields?.length > 0 ? builtDAOsTableSettings.list_fields : [], + allow_csv_export: allowCsvExport, + allow_csv_import: allowCsvImport, + can_delete: can_delete, + can_update: can_update, + can_add: can_add, + columns_view: builtDAOsTableSettings?.columns_view ? builtDAOsTableSettings.columns_view : [], + ordering_field: builtDAOsTableSettings.ordering_field ? builtDAOsTableSettings.ordering_field : undefined, }, }; } 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 45c46255d..8c533ba5e 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 @@ -250,16 +250,16 @@ export class GetTableRowsUseCase extends AbstractUseCase 0 ? builtDAOsTableSettings.sortable_by : [], - ordering: personalTableSettings.ordering ? personalTableSettings.ordering : undefined, + ordering: builtDAOsTableSettings.ordering ? builtDAOsTableSettings.ordering : undefined, identity_column: builtDAOsTableSettings.identity_column ? builtDAOsTableSettings.identity_column : null, - list_fields: personalTableSettings?.list_fields?.length > 0 ? personalTableSettings.list_fields : [], + list_fields: builtDAOsTableSettings?.list_fields?.length > 0 ? builtDAOsTableSettings.list_fields : [], allow_csv_export: allowCsvExport, allow_csv_import: allowCsvImport, can_delete: can_delete, can_update: can_update, can_add: can_add, - columns_view: personalTableSettings?.columns_view ? personalTableSettings.columns_view : [], - ordering_field: personalTableSettings.ordering_field ? personalTableSettings.ordering_field : undefined, + columns_view: builtDAOsTableSettings?.columns_view ? builtDAOsTableSettings.columns_view : [], + ordering_field: builtDAOsTableSettings.ordering_field ? builtDAOsTableSettings.ordering_field : undefined, }, }; diff --git a/backend/src/entities/table/use-cases/update-row-in-table.use.case.ts b/backend/src/entities/table/use-cases/update-row-in-table.use.case.ts index b6f7d0b63..1a578101b 100644 --- a/backend/src/entities/table/use-cases/update-row-in-table.use.case.ts +++ b/backend/src/entities/table/use-cases/update-row-in-table.use.case.ts @@ -277,6 +277,11 @@ export class UpdateRowInTableUseCase let updatedRow = await dao.getRowByPrimaryKey(tableName, futurePrimaryKey, builtDAOsTableSettings, userEmail); updatedRow = removePasswordsFromRowsUtil(updatedRow, tableWidgets); updatedRow = convertBinaryDataInRowUtil(updatedRow, tableStructure); + const allowCsvExport = tableSettings?.allow_csv_export ?? true; + const allowCsvImport = tableSettings?.allow_csv_import ?? true; + const can_delete = tableSettings?.can_delete ?? true; + const can_update = tableSettings?.can_update ?? true; + const can_add = tableSettings?.can_add ?? true; return { row: updatedRow, foreignKeys: foreignKeysWithAutocompleteColumns, @@ -293,16 +298,17 @@ export class UpdateRowInTableUseCase 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, + sortable_by: builtDAOsTableSettings?.sortable_by?.length > 0 ? builtDAOsTableSettings.sortable_by : [], + ordering: builtDAOsTableSettings.ordering ? builtDAOsTableSettings.ordering : undefined, + identity_column: builtDAOsTableSettings.identity_column ? builtDAOsTableSettings.identity_column : null, + list_fields: builtDAOsTableSettings?.list_fields?.length > 0 ? builtDAOsTableSettings.list_fields : [], + allow_csv_export: allowCsvExport, + allow_csv_import: allowCsvImport, + can_delete: can_delete, + can_update: can_update, + can_add: can_add, + columns_view: builtDAOsTableSettings?.columns_view ? builtDAOsTableSettings.columns_view : [], + ordering_field: builtDAOsTableSettings.ordering_field ? builtDAOsTableSettings.ordering_field : undefined, }, }; } catch (e) { diff --git a/backend/test/ava-tests/non-saas-tests/non-saas-table-settings-e2e.test.ts b/backend/test/ava-tests/non-saas-tests/non-saas-table-settings-e2e.test.ts index 76a20e48d..452d3f9d2 100644 --- a/backend/test/ava-tests/non-saas-tests/non-saas-table-settings-e2e.test.ts +++ b/backend/test/ava-tests/non-saas-tests/non-saas-table-settings-e2e.test.ts @@ -27,758 +27,993 @@ let _testUtils: TestUtils; let currentTest; test.before(async () => { - setSaasEnvVariable(); - const moduleFixture = await Test.createTestingModule({ - imports: [ApplicationModule, DatabaseModule], - providers: [DatabaseService, TestUtils], - }).compile(); - app = moduleFixture.createNestApplication(); - _testUtils = moduleFixture.get(TestUtils); - - app.use(cookieParser()); - app.useGlobalFilters(new AllExceptionsFilter(app.get(WinstonLogger))); - app.useGlobalPipes( - new ValidationPipe({ - exceptionFactory(validationErrors: ValidationError[] = []) { - return new ValidationException(validationErrors); - }, - }), - ); - await app.init(); - app.getHttpServer().listen(0); + setSaasEnvVariable(); + const moduleFixture = await Test.createTestingModule({ + imports: [ApplicationModule, DatabaseModule], + providers: [DatabaseService, TestUtils], + }).compile(); + app = moduleFixture.createNestApplication(); + _testUtils = moduleFixture.get(TestUtils); + + app.use(cookieParser()); + app.useGlobalFilters(new AllExceptionsFilter(app.get(WinstonLogger))); + app.useGlobalPipes( + new ValidationPipe({ + exceptionFactory(validationErrors: ValidationError[] = []) { + return new ValidationException(validationErrors); + }, + }), + ); + await app.init(); + app.getHttpServer().listen(0); }); test.after(async () => { - try { - await Cacher.clearAllCache(); - await app.close(); - } catch (e) { - console.error('After tests error ' + e); - } + try { + await Cacher.clearAllCache(); + await app.close(); + } catch (e) { + console.error('After tests error ' + e); + } }); currentTest = 'GET /settings/'; test.serial(`${currentTest} should throw an exception when tableName is missing`, async (t) => { - try { - const { token } = await registerUserAndReturnUserInfo(app); - const newConnection = getTestData(mockFactory).newConnectionToTestDB; - const createdConnection = await request(app.getHttpServer()) - .post('/connection') - .send(newConnection) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - - const connectionId = JSON.parse(createdConnection.text).id; - - const tableName = ''; - const findSettingsResponce = await request(app.getHttpServer()) - .get(`/settings/?connectionId=${connectionId}&tableName=${tableName}`) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - - t.is(findSettingsResponce.status, 400); - const findSettingsRO = JSON.parse(findSettingsResponce.text); - t.is(findSettingsRO.message, Messages.TABLE_NAME_MISSING); - } catch (e) { - console.error(e); - } + try { + const { token } = await registerUserAndReturnUserInfo(app); + const newConnection = getTestData(mockFactory).newConnectionToTestDB; + const createdConnection = await request(app.getHttpServer()) + .post('/connection') + .send(newConnection) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + const connectionId = JSON.parse(createdConnection.text).id; + + const tableName = ''; + const findSettingsResponce = await request(app.getHttpServer()) + .get(`/settings/?connectionId=${connectionId}&tableName=${tableName}`) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + t.is(findSettingsResponce.status, 400); + const findSettingsRO = JSON.parse(findSettingsResponce.text); + t.is(findSettingsRO.message, Messages.TABLE_NAME_MISSING); + } catch (e) { + console.error(e); + } }); test.serial(`${currentTest} should throw an exception when connectionId is missing`, async (t) => { - try { - const newConnection = getTestData(mockFactory).newConnectionToTestDB; - const { token } = await registerUserAndReturnUserInfo(app); - - const _createdConnection = await request(app.getHttpServer()) - .post('/connection') - .send(newConnection) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - - const connectionId = ''; - const tableName = faker.lorem.words(); - const findSettingsResponce = await request(app.getHttpServer()) - .get(`/settings/?connectionId=${connectionId}&tableName=${tableName}`) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - - t.is(findSettingsResponce.status, 400); - const findSettingsRO = JSON.parse(findSettingsResponce.text); - t.is(findSettingsRO.message, Messages.CONNECTION_ID_MISSING); - } catch (e) { - console.error(e); - } + try { + const newConnection = getTestData(mockFactory).newConnectionToTestDB; + const { token } = await registerUserAndReturnUserInfo(app); + + const _createdConnection = await request(app.getHttpServer()) + .post('/connection') + .send(newConnection) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + const connectionId = ''; + const tableName = faker.lorem.words(); + const findSettingsResponce = await request(app.getHttpServer()) + .get(`/settings/?connectionId=${connectionId}&tableName=${tableName}`) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + t.is(findSettingsResponce.status, 400); + const findSettingsRO = JSON.parse(findSettingsResponce.text); + t.is(findSettingsRO.message, Messages.CONNECTION_ID_MISSING); + } catch (e) { + console.error(e); + } }); test.serial( - `${currentTest} should return an empty connection settings object, when setting does not exists for this table in connection`, - async (t) => { - try { - const newConnection = getTestData(mockFactory).newConnectionToTestDB; - const { token } = await registerUserAndReturnUserInfo(app); - - const createdConnection = await request(app.getHttpServer()) - .post('/connection') - .send(newConnection) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - - const connectionId = JSON.parse(createdConnection.text).id; - - const tableName = faker.lorem.words(); - const findSettingsResponce = await request(app.getHttpServer()) - .get(`/settings/?connectionId=${connectionId}&tableName=${tableName}`) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - - t.is(findSettingsResponce.status, 200); - const findSettingsRO = JSON.parse(findSettingsResponce.text); - t.deepEqual(findSettingsRO, {}); - } catch (e) { - console.error(e); - } - }, + `${currentTest} should return an empty connection settings object, when setting does not exists for this table in connection`, + async (t) => { + try { + const newConnection = getTestData(mockFactory).newConnectionToTestDB; + const { token } = await registerUserAndReturnUserInfo(app); + + const createdConnection = await request(app.getHttpServer()) + .post('/connection') + .send(newConnection) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + const connectionId = JSON.parse(createdConnection.text).id; + + const tableName = faker.lorem.words(); + const findSettingsResponce = await request(app.getHttpServer()) + .get(`/settings/?connectionId=${connectionId}&tableName=${tableName}`) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + t.is(findSettingsResponce.status, 200); + const findSettingsRO = JSON.parse(findSettingsResponce.text); + t.deepEqual(findSettingsRO, {}); + } catch (e) { + console.error(e); + } + }, ); test.serial(`${currentTest} should return connection settings object`, async (t) => { - try { - const newConnection = getTestData(mockFactory).newConnectionToTestDB; - const { token } = await registerUserAndReturnUserInfo(app); - - const createdConnection = await request(app.getHttpServer()) - .post('/connection') - .send(newConnection) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - - const connectionId = JSON.parse(createdConnection.text).id; - - const tableName = 'connection'; - const createTableSettingsDTO = mockFactory.generateTableSettings( - connectionId, - tableName, - ['title'], - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - ); - - const createPersonalTableSettingsDTO = mockFactory.generatePersonalTableSettingsDto( - undefined, - 3, - QueryOrderingEnum.DESC, - 'port', - ); - - const createPersonalTableSettingsResponse = await request(app.getHttpServer()) - .put(`/settings/personal/${connectionId}`) - .query({ tableName: tableName }) - .send(createPersonalTableSettingsDTO) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - - t.is(createPersonalTableSettingsResponse.status, 200); - - const createTableSettingsResponse = await request(app.getHttpServer()) - .post(`/settings?connectionId=${connectionId}&tableName=${tableName}`) - .send(createTableSettingsDTO) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - t.is(createTableSettingsResponse.status, 201); - - const findSettingsResponce = await request(app.getHttpServer()) - .get(`/settings/?connectionId=${connectionId}&tableName=connection`) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - - const findSettingsRO = JSON.parse(findSettingsResponce.text); - t.is(Object.hasOwn(findSettingsRO, 'id'), true); - t.is(findSettingsRO.table_name, 'connection'); - t.is(findSettingsRO.display_name, createTableSettingsDTO.display_name); - t.deepEqual(findSettingsRO.search_fields, ['title']); - t.deepEqual(findSettingsRO.excluded_fields, []); - t.deepEqual(findSettingsRO.readonly_fields, []); - t.deepEqual(findSettingsRO.sortable_by, []); - t.deepEqual(findSettingsRO.autocomplete_columns, []); - t.deepEqual(findSettingsRO.identification_fields, []); - t.is(findSettingsRO.connection_id, connectionId); - } catch (e) { - console.error(e); - } + try { + const newConnection = getTestData(mockFactory).newConnectionToTestDB; + const { token } = await registerUserAndReturnUserInfo(app); + + const createdConnection = await request(app.getHttpServer()) + .post('/connection') + .send(newConnection) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + const connectionId = JSON.parse(createdConnection.text).id; + + const tableName = 'connection'; + const createTableSettingsDTO = mockFactory.generateTableSettings( + connectionId, + tableName, + ['title'], + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + ); + + const createPersonalTableSettingsDTO = mockFactory.generatePersonalTableSettingsDto( + undefined, + 3, + QueryOrderingEnum.DESC, + 'port', + ); + + const createPersonalTableSettingsResponse = await request(app.getHttpServer()) + .put(`/settings/personal/${connectionId}`) + .query({ tableName: tableName }) + .send(createPersonalTableSettingsDTO) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + t.is(createPersonalTableSettingsResponse.status, 200); + + const createTableSettingsResponse = await request(app.getHttpServer()) + .post(`/settings?connectionId=${connectionId}&tableName=${tableName}`) + .send(createTableSettingsDTO) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + t.is(createTableSettingsResponse.status, 201); + + const findSettingsResponce = await request(app.getHttpServer()) + .get(`/settings/?connectionId=${connectionId}&tableName=connection`) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + const findSettingsRO = JSON.parse(findSettingsResponce.text); + t.is(Object.hasOwn(findSettingsRO, 'id'), true); + t.is(findSettingsRO.table_name, 'connection'); + t.is(findSettingsRO.display_name, createTableSettingsDTO.display_name); + t.deepEqual(findSettingsRO.search_fields, ['title']); + t.deepEqual(findSettingsRO.excluded_fields, []); + t.deepEqual(findSettingsRO.readonly_fields, []); + t.deepEqual(findSettingsRO.sortable_by, []); + t.deepEqual(findSettingsRO.autocomplete_columns, []); + t.deepEqual(findSettingsRO.identification_fields, []); + t.is(findSettingsRO.connection_id, connectionId); + } catch (e) { + console.error(e); + } }); currentTest = 'POST /settings/'; test.serial(`${currentTest} should return created table settings`, async (t) => { - try { - const newConnection = getTestData(mockFactory).newConnectionToTestDB; - const { token } = await registerUserAndReturnUserInfo(app); - - const createdConnection = await request(app.getHttpServer()) - .post('/connection') - .send(newConnection) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - - const connectionId = JSON.parse(createdConnection.text).id; - - const createTableSettingsDTO = mockFactory.generateTableSettings( - connectionId, - 'connection', - ['title'], - undefined, - - undefined, - undefined, - undefined, - undefined, - undefined, - ); - - const createPersonalTableSettingsDTO = mockFactory.generatePersonalTableSettingsDto( - undefined, - 3, - QueryOrderingEnum.DESC, - 'port', - ); - - const createPersonalTableSettingsResponse = await request(app.getHttpServer()) - .put(`/settings/personal/${connectionId}`) - .query({ tableName: 'connection' }) - .send(createPersonalTableSettingsDTO) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - - t.is(createPersonalTableSettingsResponse.status, 200); - - const createTableSettingsResponse = await request(app.getHttpServer()) - .post(`/settings?connectionId=${connectionId}&tableName=connection`) - .send(createTableSettingsDTO) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - t.is(createTableSettingsResponse.status, 201); - - const findSettingsResponce = await request(app.getHttpServer()) - .get(`/settings/?connectionId=${connectionId}&tableName=connection`) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - - const findSettingsRO = JSON.parse(findSettingsResponce.text); - t.is(Object.hasOwn(findSettingsRO, 'id'), true); - t.is(findSettingsRO.table_name, 'connection'); - t.is(findSettingsRO.display_name, createTableSettingsDTO.display_name); - t.deepEqual(findSettingsRO.search_fields, ['title']); - t.deepEqual(findSettingsRO.excluded_fields, []); - t.deepEqual(findSettingsRO.readonly_fields, []); - t.deepEqual(findSettingsRO.sortable_by, []); - t.deepEqual(findSettingsRO.autocomplete_columns, []); - t.is(findSettingsRO.connection_id, connectionId); - } catch (e) { - console.error(e); - } + try { + const newConnection = getTestData(mockFactory).newConnectionToTestDB; + const { token } = await registerUserAndReturnUserInfo(app); + + const createdConnection = await request(app.getHttpServer()) + .post('/connection') + .send(newConnection) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + const connectionId = JSON.parse(createdConnection.text).id; + + const createTableSettingsDTO = mockFactory.generateTableSettings( + connectionId, + 'connection', + ['title'], + undefined, + + undefined, + undefined, + undefined, + undefined, + undefined, + ); + + const createPersonalTableSettingsDTO = mockFactory.generatePersonalTableSettingsDto( + undefined, + 3, + QueryOrderingEnum.DESC, + 'port', + ); + + const createPersonalTableSettingsResponse = await request(app.getHttpServer()) + .put(`/settings/personal/${connectionId}`) + .query({ tableName: 'connection' }) + .send(createPersonalTableSettingsDTO) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + t.is(createPersonalTableSettingsResponse.status, 200); + + const createTableSettingsResponse = await request(app.getHttpServer()) + .post(`/settings?connectionId=${connectionId}&tableName=connection`) + .send(createTableSettingsDTO) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + t.is(createTableSettingsResponse.status, 201); + + const findSettingsResponce = await request(app.getHttpServer()) + .get(`/settings/?connectionId=${connectionId}&tableName=connection`) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + const findSettingsRO = JSON.parse(findSettingsResponce.text); + t.is(Object.hasOwn(findSettingsRO, 'id'), true); + t.is(findSettingsRO.table_name, 'connection'); + t.is(findSettingsRO.display_name, createTableSettingsDTO.display_name); + t.deepEqual(findSettingsRO.search_fields, ['title']); + t.deepEqual(findSettingsRO.excluded_fields, []); + t.deepEqual(findSettingsRO.readonly_fields, []); + t.deepEqual(findSettingsRO.sortable_by, []); + t.deepEqual(findSettingsRO.autocomplete_columns, []); + t.is(findSettingsRO.connection_id, connectionId); + } catch (e) { + console.error(e); + } }); test.serial(`${currentTest} should throw exception when tableName is missing`, async (t) => { - try { - const newConnection = getTestData(mockFactory).newConnectionToTestDB; - const { token } = await registerUserAndReturnUserInfo(app); - - const createdConnection = await request(app.getHttpServer()) - .post('/connection') - .send(newConnection) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - - const connectionId = JSON.parse(createdConnection.text).id; - - const createTableSettingsDTO = mockFactory.generateTableSettings( - connectionId, - 'connection', - ['title'], - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - ); - - const createPersonalTableSettingsDTO = mockFactory.generatePersonalTableSettingsDto( - undefined, - 3, - QueryOrderingEnum.DESC, - 'port', - ); - - const createPersonalTableSettingsResponse = await request(app.getHttpServer()) - .put(`/settings/personal/${connectionId}`) - .query({ tableName: 'connection' }) - .send(createPersonalTableSettingsDTO) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - - t.is(createPersonalTableSettingsResponse.status, 200); - const tableName = ''; - const createTableSettingsResponse = await request(app.getHttpServer()) - .post(`/settings?connectionId=${connectionId}&tableName=${tableName}`) - .send(createTableSettingsDTO) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - t.is(createTableSettingsResponse.status, 400); - const createTableSettingsRO = JSON.parse(createTableSettingsResponse.text); - t.is(createTableSettingsRO.message, Messages.TABLE_NAME_MISSING); - } catch (e) { - console.error(e); - } + try { + const newConnection = getTestData(mockFactory).newConnectionToTestDB; + const { token } = await registerUserAndReturnUserInfo(app); + + const createdConnection = await request(app.getHttpServer()) + .post('/connection') + .send(newConnection) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + const connectionId = JSON.parse(createdConnection.text).id; + + const createTableSettingsDTO = mockFactory.generateTableSettings( + connectionId, + 'connection', + ['title'], + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + ); + + const createPersonalTableSettingsDTO = mockFactory.generatePersonalTableSettingsDto( + undefined, + 3, + QueryOrderingEnum.DESC, + 'port', + ); + + const createPersonalTableSettingsResponse = await request(app.getHttpServer()) + .put(`/settings/personal/${connectionId}`) + .query({ tableName: 'connection' }) + .send(createPersonalTableSettingsDTO) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + t.is(createPersonalTableSettingsResponse.status, 200); + const tableName = ''; + const createTableSettingsResponse = await request(app.getHttpServer()) + .post(`/settings?connectionId=${connectionId}&tableName=${tableName}`) + .send(createTableSettingsDTO) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + t.is(createTableSettingsResponse.status, 400); + const createTableSettingsRO = JSON.parse(createTableSettingsResponse.text); + t.is(createTableSettingsRO.message, Messages.TABLE_NAME_MISSING); + } catch (e) { + console.error(e); + } }); test.serial(`${currentTest} should throw exception when connectionId is missing`, async (t) => { - try { - const newConnection = getTestData(mockFactory).newConnectionToTestDB; - const { token } = await registerUserAndReturnUserInfo(app); - - const _createdConnection = await request(app.getHttpServer()) - .post('/connection') - .send(newConnection) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - - const connectionId = ''; - - const createTableSettingsDTO = mockFactory.generateTableSettings( - connectionId, - 'connection', - ['title'], - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, - ); - - const tableName = faker.lorem.words(1); - const createTableSettingsResponse = await request(app.getHttpServer()) - .post(`/settings?connectionId=${connectionId}&tableName=${tableName}`) - .send(createTableSettingsDTO) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - t.is(createTableSettingsResponse.status, 400); - const createTableSettingsRO = JSON.parse(createTableSettingsResponse.text); - t.is(createTableSettingsRO.message, Messages.CONNECTION_ID_MISSING); - } catch (e) { - console.error(e); - } + try { + const newConnection = getTestData(mockFactory).newConnectionToTestDB; + const { token } = await registerUserAndReturnUserInfo(app); + + const _createdConnection = await request(app.getHttpServer()) + .post('/connection') + .send(newConnection) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + const connectionId = ''; + + const createTableSettingsDTO = mockFactory.generateTableSettings( + connectionId, + 'connection', + ['title'], + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + ); + + const tableName = faker.lorem.words(1); + const createTableSettingsResponse = await request(app.getHttpServer()) + .post(`/settings?connectionId=${connectionId}&tableName=${tableName}`) + .send(createTableSettingsDTO) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + t.is(createTableSettingsResponse.status, 400); + const createTableSettingsRO = JSON.parse(createTableSettingsResponse.text); + t.is(createTableSettingsRO.message, Messages.CONNECTION_ID_MISSING); + } catch (e) { + console.error(e); + } }); test.serial(`${currentTest} should throw exception when search_fields is not an array`, async (t) => { - try { - const newConnection = getTestData(mockFactory).newConnectionToTestDB; - const { token } = await registerUserAndReturnUserInfo(app); - - const createdConnection = await request(app.getHttpServer()) - .post('/connection') - .send(newConnection) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - - const connectionId = JSON.parse(createdConnection.text).id; - - const createTableSettingsDTO = mockFactory.generateTableSettingsWithoutTypes( - connectionId, - 'connection', - 'title', - undefined, - undefined, - undefined, - undefined, - ); - - const createPersonalTableSettingsDTO = mockFactory.generatePersonalTableSettingsDto( - undefined, - 3, - QueryOrderingEnum.DESC, - 'port', - ); - - const createPersonalTableSettingsResponse = await request(app.getHttpServer()) - .put(`/settings/personal/${connectionId}`) - .query({ tableName: 'connection' }) - .send(createPersonalTableSettingsDTO) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - - t.is(createPersonalTableSettingsResponse.status, 200); - - const tableName = 'connection'; - const createTableSettingsResponse = await request(app.getHttpServer()) - .post(`/settings?connectionId=${connectionId}&tableName=${tableName}`) - .send(createTableSettingsDTO) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - t.is(createTableSettingsResponse.status, 400); - const createTableSettingsRO = JSON.parse(createTableSettingsResponse.text); - t.is(createTableSettingsRO.message, 'The field "search_fields" must be an array'); - } catch (e) { - console.error(e); - } + try { + const newConnection = getTestData(mockFactory).newConnectionToTestDB; + const { token } = await registerUserAndReturnUserInfo(app); + + const createdConnection = await request(app.getHttpServer()) + .post('/connection') + .send(newConnection) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + const connectionId = JSON.parse(createdConnection.text).id; + + const createTableSettingsDTO = mockFactory.generateTableSettingsWithoutTypes( + connectionId, + 'connection', + 'title', + undefined, + undefined, + undefined, + undefined, + ); + + const createPersonalTableSettingsDTO = mockFactory.generatePersonalTableSettingsDto( + undefined, + 3, + QueryOrderingEnum.DESC, + 'port', + ); + + const createPersonalTableSettingsResponse = await request(app.getHttpServer()) + .put(`/settings/personal/${connectionId}`) + .query({ tableName: 'connection' }) + .send(createPersonalTableSettingsDTO) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + t.is(createPersonalTableSettingsResponse.status, 200); + + const tableName = 'connection'; + const createTableSettingsResponse = await request(app.getHttpServer()) + .post(`/settings?connectionId=${connectionId}&tableName=${tableName}`) + .send(createTableSettingsDTO) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + t.is(createTableSettingsResponse.status, 400); + const createTableSettingsRO = JSON.parse(createTableSettingsResponse.text); + t.is(createTableSettingsRO.message, 'The field "search_fields" must be an array'); + } catch (e) { + console.error(e); + } }); test.serial(`${currentTest} should throw exception when excluded_fields is not an array`, async (t) => { - try { - const newConnection = getTestData(mockFactory).newConnectionToTestDB; - const { token } = await registerUserAndReturnUserInfo(app); - - const createdConnection = await request(app.getHttpServer()) - .post('/connection') - .send(newConnection) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - - const connectionId = JSON.parse(createdConnection.text).id; - - const createTableSettingsDTO = mockFactory.generateTableSettingsWithoutTypes( - connectionId, - 'connection', - ['title'], - 'type', - undefined, - undefined, - undefined, - ); - - const createPersonalTableSettingsDTO = mockFactory.generatePersonalTableSettingsDto( - undefined, - 3, - QueryOrderingEnum.DESC, - 'port', - ); - - const createPersonalTableSettingsResponse = await request(app.getHttpServer()) - .put(`/settings/personal/${connectionId}`) - .query({ tableName: 'connection' }) - .send(createPersonalTableSettingsDTO) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - - t.is(createPersonalTableSettingsResponse.status, 200); - - const tableName = 'connection'; - const createTableSettingsResponse = await request(app.getHttpServer()) - .post(`/settings?connectionId=${connectionId}&tableName=${tableName}`) - .send(createTableSettingsDTO) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - t.is(createTableSettingsResponse.status, 400); - const createTableSettingsRO = JSON.parse(createTableSettingsResponse.text); - t.is(createTableSettingsRO.message, 'The field "excluded_fields" must be an array'); - } catch (e) { - console.error(e); - } + try { + const newConnection = getTestData(mockFactory).newConnectionToTestDB; + const { token } = await registerUserAndReturnUserInfo(app); + + const createdConnection = await request(app.getHttpServer()) + .post('/connection') + .send(newConnection) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + const connectionId = JSON.parse(createdConnection.text).id; + + const createTableSettingsDTO = mockFactory.generateTableSettingsWithoutTypes( + connectionId, + 'connection', + ['title'], + 'type', + undefined, + undefined, + undefined, + ); + + const createPersonalTableSettingsDTO = mockFactory.generatePersonalTableSettingsDto( + undefined, + 3, + QueryOrderingEnum.DESC, + 'port', + ); + + const createPersonalTableSettingsResponse = await request(app.getHttpServer()) + .put(`/settings/personal/${connectionId}`) + .query({ tableName: 'connection' }) + .send(createPersonalTableSettingsDTO) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + t.is(createPersonalTableSettingsResponse.status, 200); + + const tableName = 'connection'; + const createTableSettingsResponse = await request(app.getHttpServer()) + .post(`/settings?connectionId=${connectionId}&tableName=${tableName}`) + .send(createTableSettingsDTO) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + t.is(createTableSettingsResponse.status, 400); + const createTableSettingsRO = JSON.parse(createTableSettingsResponse.text); + t.is(createTableSettingsRO.message, 'The field "excluded_fields" must be an array'); + } catch (e) { + console.error(e); + } }); test.serial(`${currentTest} should throw exception when sortable_by is not an array`, async (t) => { - try { - const newConnection = getTestData(mockFactory).newConnectionToTestDB; - const { token } = await registerUserAndReturnUserInfo(app); - - const createdConnection = await request(app.getHttpServer()) - .post('/connection') - .send(newConnection) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - - const connectionId = JSON.parse(createdConnection.text).id; - - const createTableSettingsDTO = mockFactory.generateTableSettingsWithoutTypes( - connectionId, - 'connection', - ['title'], - undefined, - undefined, - 'type', - undefined, - ); - - const createPersonalTableSettingsDTO = mockFactory.generatePersonalTableSettingsDto( - undefined, - 3, - QueryOrderingEnum.DESC, - 'port', - ); - - const createPersonalTableSettingsResponse = await request(app.getHttpServer()) - .put(`/settings/personal/${connectionId}`) - .query({ tableName: 'connection' }) - .send(createPersonalTableSettingsDTO) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - - t.is(createPersonalTableSettingsResponse.status, 200); - - const tableName = 'connection'; - const createTableSettingsResponse = await request(app.getHttpServer()) - .post(`/settings?connectionId=${connectionId}&tableName=${tableName}`) - .send(createTableSettingsDTO) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - t.is(createTableSettingsResponse.status, 400); - const createTableSettingsRO = JSON.parse(createTableSettingsResponse.text); - t.is(createTableSettingsRO.message, 'The field "sortable_by" must be an array'); - } catch (e) { - console.error(e); - } + try { + const newConnection = getTestData(mockFactory).newConnectionToTestDB; + const { token } = await registerUserAndReturnUserInfo(app); + + const createdConnection = await request(app.getHttpServer()) + .post('/connection') + .send(newConnection) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + const connectionId = JSON.parse(createdConnection.text).id; + + const createTableSettingsDTO = mockFactory.generateTableSettingsWithoutTypes( + connectionId, + 'connection', + ['title'], + undefined, + undefined, + 'type', + undefined, + ); + + const createPersonalTableSettingsDTO = mockFactory.generatePersonalTableSettingsDto( + undefined, + 3, + QueryOrderingEnum.DESC, + 'port', + ); + + const createPersonalTableSettingsResponse = await request(app.getHttpServer()) + .put(`/settings/personal/${connectionId}`) + .query({ tableName: 'connection' }) + .send(createPersonalTableSettingsDTO) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + t.is(createPersonalTableSettingsResponse.status, 200); + + const tableName = 'connection'; + const createTableSettingsResponse = await request(app.getHttpServer()) + .post(`/settings?connectionId=${connectionId}&tableName=${tableName}`) + .send(createTableSettingsDTO) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + t.is(createTableSettingsResponse.status, 400); + const createTableSettingsRO = JSON.parse(createTableSettingsResponse.text); + t.is(createTableSettingsRO.message, 'The field "sortable_by" must be an array'); + } catch (e) { + console.error(e); + } }); test.serial( - `${currentTest} should throw exception when there are no such field in the table for searching`, - async (t) => { - try { - const newConnection = getTestData(mockFactory).newConnectionToTestDB; - const { token } = await registerUserAndReturnUserInfo(app); - - const createdConnection = await request(app.getHttpServer()) - .post('/connection') - .send(newConnection) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - - const connectionId = JSON.parse(createdConnection.text).id; - - const createTableSettingsDTO = mockFactory.generateTableSettingsWithoutTypes( - connectionId, - 'connection', - ['testField'], - undefined, - - undefined, - undefined, - undefined, - ); - - const createPersonalTableSettingsDTO = mockFactory.generatePersonalTableSettingsDto( - undefined, - 3, - QueryOrderingEnum.DESC, - 'port', - ); - - const createPersonalTableSettingsResponse = await request(app.getHttpServer()) - .put(`/settings/personal/${connectionId}`) - .query({ tableName: 'connection' }) - .send(createPersonalTableSettingsDTO) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - - t.is(createPersonalTableSettingsResponse.status, 200); - - const tableName = 'connection'; - const createTableSettingsResponse = await request(app.getHttpServer()) - .post(`/settings?connectionId=${connectionId}&tableName=${tableName}`) - .send(createTableSettingsDTO) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - t.is(createTableSettingsResponse.status, 400); - const createTableSettingsRO = JSON.parse(createTableSettingsResponse.text); - t.is(createTableSettingsRO.message, 'There are no such fields: testField - in the table "connection"'); - } catch (e) { - console.error(e); - } - }, + `${currentTest} should throw exception when there are no such field in the table for searching`, + async (t) => { + try { + const newConnection = getTestData(mockFactory).newConnectionToTestDB; + const { token } = await registerUserAndReturnUserInfo(app); + + const createdConnection = await request(app.getHttpServer()) + .post('/connection') + .send(newConnection) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + const connectionId = JSON.parse(createdConnection.text).id; + + const createTableSettingsDTO = mockFactory.generateTableSettingsWithoutTypes( + connectionId, + 'connection', + ['testField'], + undefined, + + undefined, + undefined, + undefined, + ); + + const createPersonalTableSettingsDTO = mockFactory.generatePersonalTableSettingsDto( + undefined, + 3, + QueryOrderingEnum.DESC, + 'port', + ); + + const createPersonalTableSettingsResponse = await request(app.getHttpServer()) + .put(`/settings/personal/${connectionId}`) + .query({ tableName: 'connection' }) + .send(createPersonalTableSettingsDTO) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + t.is(createPersonalTableSettingsResponse.status, 200); + + const tableName = 'connection'; + const createTableSettingsResponse = await request(app.getHttpServer()) + .post(`/settings?connectionId=${connectionId}&tableName=${tableName}`) + .send(createTableSettingsDTO) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + t.is(createTableSettingsResponse.status, 400); + const createTableSettingsRO = JSON.parse(createTableSettingsResponse.text); + t.is(createTableSettingsRO.message, 'There are no such fields: testField - in the table "connection"'); + } catch (e) { + console.error(e); + } + }, ); test.serial( - `${currentTest} should throw exception when there are no such field in the table for excluding`, - async (t) => { - try { - const newConnection = getTestData(mockFactory).newConnectionToTestDB; - const { token } = await registerUserAndReturnUserInfo(app); - - const createdConnection = await request(app.getHttpServer()) - .post('/connection') - .send(newConnection) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - - const connectionId = JSON.parse(createdConnection.text).id; - - const createTableSettingsDTO = mockFactory.generateTableSettingsWithoutTypes( - connectionId, - 'connection', - ['type'], - ['testField'], - undefined, - undefined, - undefined, - ); - - const createPersonalTableSettingsDTO = mockFactory.generatePersonalTableSettingsDto( - undefined, - 3, - QueryOrderingEnum.DESC, - 'port', - ); - - const createPersonalTableSettingsResponse = await request(app.getHttpServer()) - .put(`/settings/personal/${connectionId}`) - .query({ tableName: 'connection' }) - .send(createPersonalTableSettingsDTO) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - - t.is(createPersonalTableSettingsResponse.status, 200); - - const tableName = 'connection'; - const createTableSettingsResponse = await request(app.getHttpServer()) - .post(`/settings?connectionId=${connectionId}&tableName=${tableName}`) - .send(createTableSettingsDTO) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - t.is(createTableSettingsResponse.status, 400); - const createTableSettingsRO = JSON.parse(createTableSettingsResponse.text); - t.is(createTableSettingsRO.message, 'There are no such fields: testField - in the table "connection"'); - } catch (e) { - console.error(e); - } - }, + `${currentTest} should throw exception when there are no such field in the table for excluding`, + async (t) => { + try { + const newConnection = getTestData(mockFactory).newConnectionToTestDB; + const { token } = await registerUserAndReturnUserInfo(app); + + const createdConnection = await request(app.getHttpServer()) + .post('/connection') + .send(newConnection) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + const connectionId = JSON.parse(createdConnection.text).id; + + const createTableSettingsDTO = mockFactory.generateTableSettingsWithoutTypes( + connectionId, + 'connection', + ['type'], + ['testField'], + undefined, + undefined, + undefined, + ); + + const createPersonalTableSettingsDTO = mockFactory.generatePersonalTableSettingsDto( + undefined, + 3, + QueryOrderingEnum.DESC, + 'port', + ); + + const createPersonalTableSettingsResponse = await request(app.getHttpServer()) + .put(`/settings/personal/${connectionId}`) + .query({ tableName: 'connection' }) + .send(createPersonalTableSettingsDTO) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + t.is(createPersonalTableSettingsResponse.status, 200); + + const tableName = 'connection'; + const createTableSettingsResponse = await request(app.getHttpServer()) + .post(`/settings?connectionId=${connectionId}&tableName=${tableName}`) + .send(createTableSettingsDTO) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + t.is(createTableSettingsResponse.status, 400); + const createTableSettingsRO = JSON.parse(createTableSettingsResponse.text); + t.is(createTableSettingsRO.message, 'There are no such fields: testField - in the table "connection"'); + } catch (e) { + console.error(e); + } + }, ); test.serial( - `${currentTest} should throw exception when there are no such field in the table for read only`, - async (t) => { - try { - const newConnection = getTestData(mockFactory).newConnectionToTestDB; - const { token } = await registerUserAndReturnUserInfo(app); - - const createdConnection = await request(app.getHttpServer()) - .post('/connection') - .send(newConnection) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - - const connectionId = JSON.parse(createdConnection.text).id; - - const createTableSettingsDTO = mockFactory.generateTableSettingsWithoutTypes( - connectionId, - 'connection', - ['type'], - undefined, - ['testField'], - undefined, - undefined, - ); - - const tableName = 'connection'; - const createTableSettingsResponse = await request(app.getHttpServer()) - .post(`/settings?connectionId=${connectionId}&tableName=${tableName}`) - .send(createTableSettingsDTO) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - - t.is(createTableSettingsResponse.status, 400); - const createTableSettingsRO = JSON.parse(createTableSettingsResponse.text); - t.is(createTableSettingsRO.message, 'There are no such fields: testField - in the table "connection"'); - } catch (e) { - console.error(e); - } - }, + `${currentTest} should throw exception when there are no such field in the table for read only`, + async (t) => { + try { + const newConnection = getTestData(mockFactory).newConnectionToTestDB; + const { token } = await registerUserAndReturnUserInfo(app); + + const createdConnection = await request(app.getHttpServer()) + .post('/connection') + .send(newConnection) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + const connectionId = JSON.parse(createdConnection.text).id; + + const createTableSettingsDTO = mockFactory.generateTableSettingsWithoutTypes( + connectionId, + 'connection', + ['type'], + undefined, + ['testField'], + undefined, + undefined, + ); + + const tableName = 'connection'; + const createTableSettingsResponse = await request(app.getHttpServer()) + .post(`/settings?connectionId=${connectionId}&tableName=${tableName}`) + .send(createTableSettingsDTO) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + t.is(createTableSettingsResponse.status, 400); + const createTableSettingsRO = JSON.parse(createTableSettingsResponse.text); + t.is(createTableSettingsRO.message, 'There are no such fields: testField - in the table "connection"'); + } catch (e) { + console.error(e); + } + }, ); test.serial( - `${currentTest} should throw exception when there are no such field in the table for sorting`, - async (t) => { - try { - const newConnection = getTestData(mockFactory).newConnectionToTestDB; - const { token } = await registerUserAndReturnUserInfo(app); - - const createdConnection = await request(app.getHttpServer()) - .post('/connection') - .send(newConnection) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - - const connectionId = JSON.parse(createdConnection.text).id; - - const createTableSettingsDTO = mockFactory.generateTableSettingsWithoutTypes( - connectionId, - 'connection', - ['type'], - undefined, - undefined, - ['testField'], - undefined, - ); - - const createPersonalTableSettingsDTO = mockFactory.generatePersonalTableSettingsDto( - undefined, - 3, - QueryOrderingEnum.DESC, - 'port', - ); - - const createPersonalTableSettingsResponse = await request(app.getHttpServer()) - .put(`/settings/personal/${connectionId}`) - .query({ tableName: 'connection' }) - .send(createPersonalTableSettingsDTO) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - - t.is(createPersonalTableSettingsResponse.status, 200); - - const tableName = 'connection'; - const createTableSettingsResponse = await request(app.getHttpServer()) - .post(`/settings?connectionId=${connectionId}&tableName=${tableName}`) - .send(createTableSettingsDTO) - .set('Cookie', token) - .set('Content-Type', 'application/json') - .set('Accept', 'application/json'); - t.is(createTableSettingsResponse.status, 400); - const createTableSettingsRO = JSON.parse(createTableSettingsResponse.text); - t.is(createTableSettingsRO.message, 'There are no such fields: testField - in the table "connection"'); - } catch (e) { - console.error(e); - } - }, + `${currentTest} should throw exception when there are no such field in the table for sorting`, + async (t) => { + try { + const newConnection = getTestData(mockFactory).newConnectionToTestDB; + const { token } = await registerUserAndReturnUserInfo(app); + + const createdConnection = await request(app.getHttpServer()) + .post('/connection') + .send(newConnection) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + const connectionId = JSON.parse(createdConnection.text).id; + + const createTableSettingsDTO = mockFactory.generateTableSettingsWithoutTypes( + connectionId, + 'connection', + ['type'], + undefined, + undefined, + ['testField'], + undefined, + ); + + const createPersonalTableSettingsDTO = mockFactory.generatePersonalTableSettingsDto( + undefined, + 3, + QueryOrderingEnum.DESC, + 'port', + ); + + const createPersonalTableSettingsResponse = await request(app.getHttpServer()) + .put(`/settings/personal/${connectionId}`) + .query({ tableName: 'connection' }) + .send(createPersonalTableSettingsDTO) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + t.is(createPersonalTableSettingsResponse.status, 200); + + const tableName = 'connection'; + const createTableSettingsResponse = await request(app.getHttpServer()) + .post(`/settings?connectionId=${connectionId}&tableName=${tableName}`) + .send(createTableSettingsDTO) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + t.is(createTableSettingsResponse.status, 400); + const createTableSettingsRO = JSON.parse(createTableSettingsResponse.text); + t.is(createTableSettingsRO.message, 'There are no such fields: testField - in the table "connection"'); + } catch (e) { + console.error(e); + } + }, +); + +currentTest = 'GET /table/rows/:slug personal settings priority'; + +test.skip( + `${currentTest} should use personal table settings over common table settings when both exist`, + async (t) => { + try { + const newConnection = getTestData(mockFactory).newConnectionToTestDB; + const { token } = await registerUserAndReturnUserInfo(app); + + const createdConnection = await request(app.getHttpServer()) + .post('/connection') + .send(newConnection) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + const connectionId = JSON.parse(createdConnection.text).id; + const tableName = 'connection'; + + // Create common table settings with specific list_fields + const createTableSettingsDTO = mockFactory.generateTableSettings( + connectionId, + tableName, + ['title'], + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + ); + createTableSettingsDTO.list_fields = ['id', 'title', 'type']; + createTableSettingsDTO.list_per_page = 10; + createTableSettingsDTO.ordering = QueryOrderingEnum.ASC; + createTableSettingsDTO.ordering_field = 'id'; + + const createTableSettingsResponse = await request(app.getHttpServer()) + .post(`/settings?connectionId=${connectionId}&tableName=${tableName}`) + .send(createTableSettingsDTO) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + t.is(createTableSettingsResponse.status, 201); + + // Create personal table settings with different values + const createPersonalTableSettingsDTO = mockFactory.generatePersonalTableSettingsDto( + ['id', 'title'], // different list_fields + 5, // different list_per_page + QueryOrderingEnum.DESC, // different ordering + 'title', // different ordering_field + ); + + const createPersonalTableSettingsResponse = await request(app.getHttpServer()) + .put(`/settings/personal/${connectionId}`) + .query({ tableName: tableName }) + .send(createPersonalTableSettingsDTO) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + t.is(createPersonalTableSettingsResponse.status, 200); + + // Get table rows and verify personal settings are applied + const getTableRowsResponse = await request(app.getHttpServer()) + .get(`/table/rows/${connectionId}?tableName=${tableName}`) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + t.is(getTableRowsResponse.status, 200); + const getTableRowsRO = JSON.parse(getTableRowsResponse.text); + + // Verify that personal settings list_per_page is used (5 instead of 10) + t.is(getTableRowsRO.pagination.perPage, 5); + + // Verify that personal list_fields are used - rows should only have id and title + const rowKeys = Object.keys(getTableRowsRO.rows[0]); + t.true(rowKeys.includes('id')); + t.true(rowKeys.includes('title')); + t.false(rowKeys.includes('type')); // type was in common settings but not in personal + } catch (e) { + console.error(e); + throw e; + } + }, +); + +test.serial(`${currentTest} should use common table settings when personal table settings do not exist`, async (t) => { + try { + const newConnection = getTestData(mockFactory).newConnectionToTestDB; + const { token } = await registerUserAndReturnUserInfo(app); + + const createdConnection = await request(app.getHttpServer()) + .post('/connection') + .send(newConnection) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + const connectionId = JSON.parse(createdConnection.text).id; + const tableName = 'connection'; + + // Create only common table settings with specific list_fields + const createTableSettingsDTO = mockFactory.generateTableSettings( + connectionId, + tableName, + ['title'], + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + ); + createTableSettingsDTO.list_fields = ['id', 'title', 'type']; + createTableSettingsDTO.list_per_page = 7; + createTableSettingsDTO.ordering = QueryOrderingEnum.ASC; + createTableSettingsDTO.ordering_field = 'id'; + + const createTableSettingsResponse = await request(app.getHttpServer()) + .post(`/settings?connectionId=${connectionId}&tableName=${tableName}`) + .send(createTableSettingsDTO) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + t.is(createTableSettingsResponse.status, 201); + + // No personal table settings created + + // Get table rows and verify common settings are applied + const getTableRowsResponse = await request(app.getHttpServer()) + .get(`/table/rows/${connectionId}?tableName=${tableName}`) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + t.is(getTableRowsResponse.status, 200); + const getTableRowsRO = JSON.parse(getTableRowsResponse.text); + + // Verify that common settings list_per_page is used (7) + t.is(getTableRowsRO.pagination.perPage, 7); + + // Verify that common list_fields are used - rows should have id, title, and type + const rowKeys = Object.keys(getTableRowsRO.rows[0]); + t.true(rowKeys.includes('id')); + t.true(rowKeys.includes('title')); + t.true(rowKeys.includes('type')); + } catch (e) { + console.error(e); + throw e; + } +}); + +test.serial( + `${currentTest} should use common table settings list_fields when personal list_fields is empty array`, + async (t) => { + try { + const newConnection = getTestData(mockFactory).newConnectionToTestDB; + const { token } = await registerUserAndReturnUserInfo(app); + + const createdConnection = await request(app.getHttpServer()) + .post('/connection') + .send(newConnection) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + const connectionId = JSON.parse(createdConnection.text).id; + const tableName = 'connection'; + + // Create common table settings with specific list_fields + const createTableSettingsDTO = mockFactory.generateTableSettings( + connectionId, + tableName, + ['title'], + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + ); + createTableSettingsDTO.list_fields = ['id', 'title']; + createTableSettingsDTO.list_per_page = 8; + + const createTableSettingsResponse = await request(app.getHttpServer()) + .post(`/settings?connectionId=${connectionId}&tableName=${tableName}`) + .send(createTableSettingsDTO) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + t.is(createTableSettingsResponse.status, 201); + + // Create personal table settings with empty list_fields but different list_per_page + const createPersonalTableSettingsDTO = mockFactory.generatePersonalTableSettingsDto( + [], // empty list_fields - should fall back to common + 4, // different list_per_page + QueryOrderingEnum.DESC, + 'title', + ); + + const createPersonalTableSettingsResponse = await request(app.getHttpServer()) + .put(`/settings/personal/${connectionId}`) + .query({ tableName: tableName }) + .send(createPersonalTableSettingsDTO) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + t.is(createPersonalTableSettingsResponse.status, 200); + + // Get table rows and verify settings are applied correctly + const getTableRowsResponse = await request(app.getHttpServer()) + .get(`/table/rows/${connectionId}?tableName=${tableName}`) + .set('Cookie', token) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + t.is(getTableRowsResponse.status, 200); + const getTableRowsRO = JSON.parse(getTableRowsResponse.text); + + // Verify that personal settings list_per_page is used (4) + t.is(getTableRowsRO.pagination.perPage, 4); + + // Verify that common list_fields are used since personal was empty + const rowKeys = Object.keys(getTableRowsRO.rows[0]); + t.true(rowKeys.includes('id')); + t.true(rowKeys.includes('title')); + } catch (e) { + console.error(e); + throw e; + } + }, ); diff --git a/backend/test/ava-tests/saas-tests/table-settings-personal-e2e.test.ts b/backend/test/ava-tests/saas-tests/table-settings-personal-e2e.test.ts index d5cf25394..0dcdc1897 100644 --- a/backend/test/ava-tests/saas-tests/table-settings-personal-e2e.test.ts +++ b/backend/test/ava-tests/saas-tests/table-settings-personal-e2e.test.ts @@ -243,3 +243,224 @@ test.serial(`${currentTest} should return empty object when personal table setti t.is(findPersonalTableSettingsResponse.status, 200); t.deepEqual(findPersonalTableSettingsRO, {}); }); + +currentTest = 'GET /table/rows/:slug personal settings priority'; + +test.skip(`${currentTest} should use personal table settings over common table settings when both exist`, async (t) => { + const connectionToTestDB = getTestData(mockFactory).connectionToPostgres; + const firstUserToken = (await registerUserAndReturnUserInfo(app)).token; + const { testTableName, testTableColumnName, testTableSecondColumnName } = await createTestTable(connectionToTestDB); + + const createConnectionResponse = await request(app.getHttpServer()) + .post('/connection') + .send(connectionToTestDB) + .set('Cookie', firstUserToken) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + const createConnectionRO = JSON.parse(createConnectionResponse.text); + t.is(createConnectionResponse.status, 201); + + // Create common table settings with specific list_fields + const createTableSettingsDTO = mockFactory.generateTableSettings( + createConnectionRO.id, + testTableName, + [testTableColumnName], + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + ); + createTableSettingsDTO.list_fields = ['id', testTableColumnName, testTableSecondColumnName]; + createTableSettingsDTO.list_per_page = 10; + createTableSettingsDTO.ordering = QueryOrderingEnum.ASC; + createTableSettingsDTO.ordering_field = 'id'; + + const createTableSettingsResponse = await request(app.getHttpServer()) + .post(`/settings?connectionId=${createConnectionRO.id}&tableName=${testTableName}`) + .send(createTableSettingsDTO) + .set('Cookie', firstUserToken) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + t.is(createTableSettingsResponse.status, 201); + + // Create personal table settings with different values + const createPersonalTableSettingsDTO: CreatePersonalTableSettingsDto = { + list_fields: ['id', testTableColumnName], // different list_fields (fewer columns) + list_per_page: 5, // different list_per_page + ordering: QueryOrderingEnum.DESC, // different ordering + ordering_field: testTableColumnName, // different ordering_field + original_names: true, + columns_view: [testTableColumnName, 'id'], + }; + + const createPersonalTableSettingsResponse = await request(app.getHttpServer()) + .put(`/settings/personal/${createConnectionRO.id}`) + .query({ tableName: testTableName }) + .send(createPersonalTableSettingsDTO) + .set('Cookie', firstUserToken) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + t.is(createPersonalTableSettingsResponse.status, 200); + + // Get table rows and verify personal settings are applied + const getTableRowsResponse = await request(app.getHttpServer()) + .get(`/table/rows/${createConnectionRO.id}?tableName=${testTableName}`) + .set('Cookie', firstUserToken) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + t.is(getTableRowsResponse.status, 200); + const getTableRowsRO = JSON.parse(getTableRowsResponse.text); + + // Verify that personal settings list_per_page is used (5 instead of 10) + t.is(getTableRowsRO.pagination.perPage, 5); + + // Verify that personal list_fields are used - rows should only have id and testTableColumnName + const rowKeys = Object.keys(getTableRowsRO.rows[0]); + t.true(rowKeys.includes('id')); + t.true(rowKeys.includes(testTableColumnName)); + // t.false(rowKeys.includes(testTableSecondColumnName)); // was in common settings but not in personal +}); + +test.serial(`${currentTest} should use common table settings when personal table settings do not exist`, async (t) => { + const connectionToTestDB = getTestData(mockFactory).connectionToPostgres; + const firstUserToken = (await registerUserAndReturnUserInfo(app)).token; + const { testTableName, testTableColumnName, testTableSecondColumnName } = await createTestTable(connectionToTestDB); + + const createConnectionResponse = await request(app.getHttpServer()) + .post('/connection') + .send(connectionToTestDB) + .set('Cookie', firstUserToken) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + const createConnectionRO = JSON.parse(createConnectionResponse.text); + t.is(createConnectionResponse.status, 201); + + // Create only common table settings with specific list_fields + const createTableSettingsDTO = mockFactory.generateTableSettings( + createConnectionRO.id, + testTableName, + [testTableColumnName], + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + ); + createTableSettingsDTO.list_fields = ['id', testTableColumnName, testTableSecondColumnName]; + createTableSettingsDTO.list_per_page = 7; + createTableSettingsDTO.ordering = QueryOrderingEnum.ASC; + createTableSettingsDTO.ordering_field = 'id'; + + const createTableSettingsResponse = await request(app.getHttpServer()) + .post(`/settings?connectionId=${createConnectionRO.id}&tableName=${testTableName}`) + .send(createTableSettingsDTO) + .set('Cookie', firstUserToken) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + t.is(createTableSettingsResponse.status, 201); + + // No personal table settings created + + // Get table rows and verify common settings are applied + const getTableRowsResponse = await request(app.getHttpServer()) + .get(`/table/rows/${createConnectionRO.id}?tableName=${testTableName}`) + .set('Cookie', firstUserToken) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + t.is(getTableRowsResponse.status, 200); + const getTableRowsRO = JSON.parse(getTableRowsResponse.text); + + // Verify that common settings list_per_page is used (7) + t.is(getTableRowsRO.pagination.perPage, 7); + + // Verify that common list_fields are used - rows should have id, testTableColumnName, and testTableSecondColumnName + const rowKeys = Object.keys(getTableRowsRO.rows[0]); + t.true(rowKeys.includes('id')); + t.true(rowKeys.includes(testTableColumnName)); + t.true(rowKeys.includes(testTableSecondColumnName)); +}); + +test.serial( + `${currentTest} should use common table settings list_fields when personal list_fields is empty array`, + async (t) => { + const connectionToTestDB = getTestData(mockFactory).connectionToPostgres; + const firstUserToken = (await registerUserAndReturnUserInfo(app)).token; + const { testTableName, testTableColumnName, testTableSecondColumnName } = await createTestTable(connectionToTestDB); + + const createConnectionResponse = await request(app.getHttpServer()) + .post('/connection') + .send(connectionToTestDB) + .set('Cookie', firstUserToken) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + const createConnectionRO = JSON.parse(createConnectionResponse.text); + t.is(createConnectionResponse.status, 201); + + // Create common table settings with specific list_fields + const createTableSettingsDTO = mockFactory.generateTableSettings( + createConnectionRO.id, + testTableName, + [testTableColumnName], + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + ); + createTableSettingsDTO.list_fields = ['id', testTableColumnName]; + createTableSettingsDTO.list_per_page = 8; + + const createTableSettingsResponse = await request(app.getHttpServer()) + .post(`/settings?connectionId=${createConnectionRO.id}&tableName=${testTableName}`) + .send(createTableSettingsDTO) + .set('Cookie', firstUserToken) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + t.is(createTableSettingsResponse.status, 201); + + // Create personal table settings with empty list_fields but different list_per_page + const createPersonalTableSettingsDTO: CreatePersonalTableSettingsDto = { + list_fields: [], // empty list_fields - should fall back to common + list_per_page: 4, // different list_per_page + ordering: QueryOrderingEnum.DESC, + ordering_field: testTableColumnName, + original_names: true, + columns_view: [testTableColumnName, 'id'], + }; + + const createPersonalTableSettingsResponse = await request(app.getHttpServer()) + .put(`/settings/personal/${createConnectionRO.id}`) + .query({ tableName: testTableName }) + .send(createPersonalTableSettingsDTO) + .set('Cookie', firstUserToken) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + t.is(createPersonalTableSettingsResponse.status, 200); + + // Get table rows and verify settings are applied correctly + const getTableRowsResponse = await request(app.getHttpServer()) + .get(`/table/rows/${createConnectionRO.id}?tableName=${testTableName}`) + .set('Cookie', firstUserToken) + .set('Content-Type', 'application/json') + .set('Accept', 'application/json'); + + t.is(getTableRowsResponse.status, 200); + const getTableRowsRO = JSON.parse(getTableRowsResponse.text); + + // Verify that personal settings list_per_page is used (4) + t.is(getTableRowsRO.pagination.perPage, 4); + + // Verify that common list_fields are used since personal was empty + const rowKeys = Object.keys(getTableRowsRO.rows[0]); + t.true(rowKeys.includes('id')); + t.true(rowKeys.includes(testTableColumnName)); + }, +); diff --git a/backend/test/mock.factory.ts b/backend/test/mock.factory.ts index 41336a2b2..3bef62c9c 100644 --- a/backend/test/mock.factory.ts +++ b/backend/test/mock.factory.ts @@ -602,7 +602,7 @@ export class MockFactory { identity_column: identity_column, allow_csv_import: allow_csv_import, allow_csv_export: allow_csv_export, - }; + } as any; /*eslint-enable*/ } diff --git a/shared-code/src/helpers/data-structures-builders/table-settings.ds.builder.ts b/shared-code/src/helpers/data-structures-builders/table-settings.ds.builder.ts index 659a9be9a..24a269f1f 100644 --- a/shared-code/src/helpers/data-structures-builders/table-settings.ds.builder.ts +++ b/shared-code/src/helpers/data-structures-builders/table-settings.ds.builder.ts @@ -38,7 +38,9 @@ export function buildDAOsTableSettingsDs( display_name: commonTableSettings?.display_name, search_fields: commonTableSettings?.search_fields, excluded_fields: commonTableSettings?.excluded_fields, - list_fields: personalTableSettings?.list_fields || commonTableSettings?.list_fields || [], + list_fields: (Array.isArray(personalTableSettings?.list_fields) && personalTableSettings.list_fields.length > 0) + ? personalTableSettings.list_fields + : commonTableSettings?.list_fields || [], identification_fields: commonTableSettings?.identification_fields, list_per_page: personalTableSettings?.list_per_page || commonTableSettings?.list_per_page, ordering: personalTableSettings?.ordering || commonTableSettings?.ordering,