diff --git a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/remote-table.service.ts b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/remote-table.service.ts index af62ed712a49..fef23b0020b7 100644 --- a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/remote-table.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/remote-table.service.ts @@ -258,13 +258,21 @@ export class RemoteTableService { workspaceId, ); - const localTableName = getRemoteTableLocalName(input.name); + const workspaceDataSource = + await this.workspaceDataSourceService.connectToWorkspaceDataSource( + workspaceId, + ); - await this.validateTableNameDoesNotExists( - localTableName, - workspaceId, - dataSourceMetatada.schema, - ); + const { baseName: localTableBaseName, suffix: localTableSuffix } = + await getRemoteTableLocalName( + input.name, + dataSourceMetatada.schema, + workspaceDataSource, + ); + + const localTableName = localTableSuffix + ? `${localTableBaseName}${localTableSuffix}` + : localTableBaseName; const remoteTableEntity = this.remoteTableRepository.create({ distantTableName: input.name, @@ -297,7 +305,8 @@ export class RemoteTableService { await this.createRemoteTableMetadata( workspaceId, - localTableName, + localTableBaseName, + localTableSuffix, distantTableColumns, distantTableIdColumn, dataSourceMetatada.id, @@ -411,27 +420,6 @@ export class RemoteTableService { await this.workspaceCacheVersionService.incrementVersion(workspaceId); } - private async validateTableNameDoesNotExists( - tableName: string, - workspaceId: string, - workspaceSchemaName: string, - ) { - const workspaceDataSource = - await this.workspaceDataSourceService.connectToWorkspaceDataSource( - workspaceId, - ); - - const numberOfTablesWithSameName = +( - await workspaceDataSource.query( - `SELECT count(table_name) FROM information_schema.tables WHERE table_name LIKE '${tableName}' AND table_schema IN ('core', 'metadata', '${workspaceSchemaName}')`, - ) - )[0].count; - - if (numberOfTablesWithSameName > 0) { - throw new BadRequestException('Table name is not available.'); - } - } - private async fetchForeignTableNamesWithinWorkspace( workspaceId: string, foreignDataWrapperId: string, @@ -525,16 +513,25 @@ export class RemoteTableService { private async createRemoteTableMetadata( workspaceId: string, - localTableName: string, + localTableBaseName: string, + localTableSuffix: number | undefined, distantTableColumns: PostgresTableSchemaColumn[], distantTableIdColumn: PostgresTableSchemaColumn, dataSourceMetadataId: string, ) { + const localTableNameSingular = localTableSuffix + ? `${localTableBaseName}${localTableSuffix}` + : localTableBaseName; + + const localTableNamePlural = localTableSuffix + ? `${plural(localTableBaseName)}${localTableSuffix}` + : plural(localTableBaseName); + const objectMetadata = await this.objectMetadataService.createOne({ - nameSingular: localTableName, - namePlural: plural(localTableName), - labelSingular: camelToTitleCase(camelCase(localTableName)), - labelPlural: camelToTitleCase(plural(camelCase(localTableName))), + nameSingular: localTableNameSingular, + namePlural: localTableNamePlural, + labelSingular: camelToTitleCase(camelCase(localTableBaseName)), + labelPlural: camelToTitleCase(plural(camelCase(localTableBaseName))), description: 'Remote table', dataSourceId: dataSourceMetadataId, workspaceId: workspaceId, @@ -571,7 +568,7 @@ export class RemoteTableService { } } catch (error) { this.logger.error( - `Could not create field ${columnName} for remote table ${localTableName}: ${error}`, + `Could not create field ${columnName} for remote table ${localTableNameSingular}: ${error}`, ); } } diff --git a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/utils/get-remote-table-local-name.util.ts b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/utils/get-remote-table-local-name.util.ts index 02b960db5a37..92008e169d00 100644 --- a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/utils/get-remote-table-local-name.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/utils/get-remote-table-local-name.util.ts @@ -1,6 +1,59 @@ +import { BadRequestException } from '@nestjs/common/exceptions'; + import { singular } from 'pluralize'; +import { DataSource } from 'typeorm'; import { camelCase } from 'src/utils/camel-case'; -export const getRemoteTableLocalName = (distantTableName: string) => - singular(camelCase(distantTableName)); +const MAX_SUFFIX = 10; + +type RemoteTableLocalName = { + baseName: string; + suffix?: number; +}; + +const isNameAvailable = async ( + tableName: string, + workspaceSchemaName: string, + workspaceDataSource: DataSource, +) => { + const numberOfTablesWithSameName = +( + await workspaceDataSource.query( + `SELECT count(table_name) FROM information_schema.tables WHERE table_name LIKE '${tableName}' AND table_schema IN ('core', 'metadata', '${workspaceSchemaName}')`, + ) + )[0].count; + + return numberOfTablesWithSameName === 0; +}; + +export const getRemoteTableLocalName = async ( + distantTableName: string, + workspaceSchemaName: string, + workspaceDataSource: DataSource, +): Promise => { + const baseName = singular(camelCase(distantTableName)); + const isBaseNameValid = await isNameAvailable( + baseName, + workspaceSchemaName, + workspaceDataSource, + ); + + if (isBaseNameValid) { + return { baseName }; + } + + for (let suffix = 2; suffix < MAX_SUFFIX; suffix++) { + const name = `${baseName}${suffix}`; + const isNameWithSuffixValid = await isNameAvailable( + name, + workspaceSchemaName, + workspaceDataSource, + ); + + if (isNameWithSuffixValid) { + return { baseName, suffix }; + } + } + + throw new BadRequestException('Table name is already taken.'); +};