Minor refacto and fixes on Remotes updates (#5438)

In this PR

- Code refactoring
- v0 of adding "updates available" info in Connection sync status
<img width="835" alt="Capture d’écran 2024-05-16 à 17 02 07"
src="https://github.com/twentyhq/twenty/assets/51697796/9674d3ca-bed2-4520-a5a6-ba37bc242d06">

- fix distant table columns with not-camel case names are always
considered as new
This commit is contained in:
Marie
2024-05-16 17:31:34 +02:00
committed by GitHub
parent 9bc9513845
commit d741f4a5bd
15 changed files with 122 additions and 104 deletions

View File

@ -250,6 +250,14 @@ export type DeleteOneRelationInput = {
id: Scalars['UUID']['input']; id: Scalars['UUID']['input'];
}; };
/** Schema update on a table */
export enum DistantTableUpdate {
ColumnsAdded = 'COLUMNS_ADDED',
ColumnsDeleted = 'COLUMNS_DELETED',
ColumnsTypeChanged = 'COLUMNS_TYPE_CHANGED',
TableDeleted = 'TABLE_DELETED'
}
export type EmailPasswordResetLink = { export type EmailPasswordResetLink = {
__typename?: 'EmailPasswordResetLink'; __typename?: 'EmailPasswordResetLink';
/** Boolean that confirms query was dispatched */ /** Boolean that confirms query was dispatched */
@ -330,8 +338,6 @@ export enum FileFolder {
export type FindManyRemoteTablesInput = { export type FindManyRemoteTablesInput = {
/** The id of the remote server. */ /** The id of the remote server. */
id: Scalars['ID']['input']; id: Scalars['ID']['input'];
/** Indicates if data from distant tables should be refreshed. */
refreshData?: InputMaybe<Scalars['Boolean']['input']>;
}; };
export type FullName = { export type FullName = {
@ -812,7 +818,7 @@ export type RemoteTable = {
id?: Maybe<Scalars['UUID']['output']>; id?: Maybe<Scalars['UUID']['output']>;
name: Scalars['String']['output']; name: Scalars['String']['output'];
schema?: Maybe<Scalars['String']['output']>; schema?: Maybe<Scalars['String']['output']>;
schemaPendingUpdates?: Maybe<Array<TableUpdate>>; schemaPendingUpdates?: Maybe<Array<DistantTableUpdate>>;
status: RemoteTableStatus; status: RemoteTableStatus;
}; };
@ -857,14 +863,6 @@ export type Support = {
supportFrontChatId?: Maybe<Scalars['String']['output']>; supportFrontChatId?: Maybe<Scalars['String']['output']>;
}; };
/** Schema update on a table */
export enum TableUpdate {
ColumnsAdded = 'COLUMNS_ADDED',
ColumnsDeleted = 'COLUMNS_DELETED',
ColumnsTypeChanged = 'COLUMNS_TYPE_CHANGED',
TableDeleted = 'TABLE_DELETED'
}
export type Telemetry = { export type Telemetry = {
__typename?: 'Telemetry'; __typename?: 'Telemetry';
anonymizationEnabled: Scalars['Boolean']['output']; anonymizationEnabled: Scalars['Boolean']['output'];
@ -1261,7 +1259,7 @@ export type RelationEdge = {
export type RemoteServerFieldsFragment = { __typename?: 'RemoteServer', id: string, createdAt: any, foreignDataWrapperId: string, foreignDataWrapperOptions?: any | null, foreignDataWrapperType: string, updatedAt: any, schema?: string | null, userMappingOptions?: { __typename?: 'UserMappingOptionsUser', user?: string | null } | null }; export type RemoteServerFieldsFragment = { __typename?: 'RemoteServer', id: string, createdAt: any, foreignDataWrapperId: string, foreignDataWrapperOptions?: any | null, foreignDataWrapperType: string, updatedAt: any, schema?: string | null, userMappingOptions?: { __typename?: 'UserMappingOptionsUser', user?: string | null } | null };
export type RemoteTableFieldsFragment = { __typename?: 'RemoteTable', id?: any | null, name: string, schema?: string | null, status: RemoteTableStatus, schemaPendingUpdates?: Array<TableUpdate> | null }; export type RemoteTableFieldsFragment = { __typename?: 'RemoteTable', id?: any | null, name: string, schema?: string | null, status: RemoteTableStatus, schemaPendingUpdates?: Array<DistantTableUpdate> | null };
export type CreateServerMutationVariables = Exact<{ export type CreateServerMutationVariables = Exact<{
input: CreateRemoteServerInput; input: CreateRemoteServerInput;
@ -1282,14 +1280,14 @@ export type SyncRemoteTableMutationVariables = Exact<{
}>; }>;
export type SyncRemoteTableMutation = { __typename?: 'Mutation', syncRemoteTable: { __typename?: 'RemoteTable', id?: any | null, name: string, schema?: string | null, status: RemoteTableStatus, schemaPendingUpdates?: Array<TableUpdate> | null } }; export type SyncRemoteTableMutation = { __typename?: 'Mutation', syncRemoteTable: { __typename?: 'RemoteTable', id?: any | null, name: string, schema?: string | null, status: RemoteTableStatus, schemaPendingUpdates?: Array<DistantTableUpdate> | null } };
export type UnsyncRemoteTableMutationVariables = Exact<{ export type UnsyncRemoteTableMutationVariables = Exact<{
input: RemoteTableInput; input: RemoteTableInput;
}>; }>;
export type UnsyncRemoteTableMutation = { __typename?: 'Mutation', unsyncRemoteTable: { __typename?: 'RemoteTable', id?: any | null, name: string, schema?: string | null, status: RemoteTableStatus, schemaPendingUpdates?: Array<TableUpdate> | null } }; export type UnsyncRemoteTableMutation = { __typename?: 'Mutation', unsyncRemoteTable: { __typename?: 'RemoteTable', id?: any | null, name: string, schema?: string | null, status: RemoteTableStatus, schemaPendingUpdates?: Array<DistantTableUpdate> | null } };
export type UpdateServerMutationVariables = Exact<{ export type UpdateServerMutationVariables = Exact<{
input: UpdateRemoteServerInput; input: UpdateRemoteServerInput;
@ -1310,7 +1308,7 @@ export type GetManyRemoteTablesQueryVariables = Exact<{
}>; }>;
export type GetManyRemoteTablesQuery = { __typename?: 'Query', findAvailableRemoteTablesByServerId: Array<{ __typename?: 'RemoteTable', id?: any | null, name: string, schema?: string | null, status: RemoteTableStatus, schemaPendingUpdates?: Array<TableUpdate> | null }> }; export type GetManyRemoteTablesQuery = { __typename?: 'Query', findAvailableRemoteTablesByServerId: Array<{ __typename?: 'RemoteTable', id?: any | null, name: string, schema?: string | null, status: RemoteTableStatus, schemaPendingUpdates?: Array<DistantTableUpdate> | null }> };
export type GetOneDatabaseConnectionQueryVariables = Exact<{ export type GetOneDatabaseConnectionQueryVariables = Exact<{
input: RemoteServerIdInput; input: RemoteServerIdInput;

View File

@ -10,13 +10,11 @@ import {
type UseGetDatabaseConnectionTablesParams = { type UseGetDatabaseConnectionTablesParams = {
connectionId: string; connectionId: string;
skip?: boolean; skip?: boolean;
refreshData?: boolean;
}; };
export const useGetDatabaseConnectionTables = ({ export const useGetDatabaseConnectionTables = ({
connectionId, connectionId,
skip, skip,
refreshData,
}: UseGetDatabaseConnectionTablesParams) => { }: UseGetDatabaseConnectionTablesParams) => {
const apolloMetadataClient = useApolloMetadataClient(); const apolloMetadataClient = useApolloMetadataClient();
@ -29,7 +27,6 @@ export const useGetDatabaseConnectionTables = ({
variables: { variables: {
input: { input: {
id: connectionId, id: connectionId,
refreshData: refreshData,
}, },
}, },
}); });

View File

@ -25,13 +25,23 @@ export const SettingsIntegrationDatabaseConnectionSyncStatus = ({
(table) => table.status === RemoteTableStatus.Synced, (table) => table.status === RemoteTableStatus.Synced,
); );
const updatesAvailable = tables.some(
(table) =>
table.schemaPendingUpdates?.length &&
table.schemaPendingUpdates.length > 0,
);
return ( return (
<Status <Status
color="green" color={updatesAvailable ? 'yellow' : 'green'}
text={ text={
syncedTables.length === 1 syncedTables.length === 1
? `1 tracked table` ? `1 tracked table${
: `${syncedTables.length} tracked tables` updatesAvailable ? ' (with pending schema updates)' : ''
}`
: `${syncedTables.length} tracked tables${
updatesAvailable ? ' (with pending schema updates)' : ''
}`
} }
/> />
); );

View File

@ -7,9 +7,9 @@ import { useUnsyncRemoteTable } from '@/databases/hooks/useUnsyncRemoteTable';
import { SettingsListCard } from '@/settings/components/SettingsListCard'; import { SettingsListCard } from '@/settings/components/SettingsListCard';
import { SettingsIntegrationRemoteTableSyncStatusToggle } from '@/settings/integrations/components/SettingsIntegrationRemoteTableSyncStatusToggle'; import { SettingsIntegrationRemoteTableSyncStatusToggle } from '@/settings/integrations/components/SettingsIntegrationRemoteTableSyncStatusToggle';
import { import {
DistantTableUpdate,
RemoteTable, RemoteTable,
RemoteTableStatus, RemoteTableStatus,
TableUpdate,
} from '~/generated-metadata/graphql'; } from '~/generated-metadata/graphql';
import { isDefined } from '~/utils/isDefined'; import { isDefined } from '~/utils/isDefined';
@ -39,20 +39,22 @@ const StyledText = styled.h3`
margin: 0; margin: 0;
`; `;
const getTableUpdatesText = (schemaPendingUpdates: TableUpdate[]) => { const getDistantTableUpdatesText = (
if (schemaPendingUpdates.includes(TableUpdate.TableDeleted)) { schemaPendingUpdates: DistantTableUpdate[],
) => {
if (schemaPendingUpdates.includes(DistantTableUpdate.TableDeleted)) {
return 'Table has been deleted'; return 'Table has been deleted';
} }
if ( if (
schemaPendingUpdates.includes(TableUpdate.ColumnsAdded) && schemaPendingUpdates.includes(DistantTableUpdate.ColumnsAdded) &&
schemaPendingUpdates.includes(TableUpdate.ColumnsDeleted) schemaPendingUpdates.includes(DistantTableUpdate.ColumnsDeleted)
) { ) {
return 'Columns have been added and other deleted'; return 'Columns have been added and other deleted';
} }
if (schemaPendingUpdates.includes(TableUpdate.ColumnsAdded)) { if (schemaPendingUpdates.includes(DistantTableUpdate.ColumnsAdded)) {
return 'Columns have been added'; return 'Columns have been added';
} }
if (schemaPendingUpdates.includes(TableUpdate.ColumnsDeleted)) { if (schemaPendingUpdates.includes(DistantTableUpdate.ColumnsDeleted)) {
return 'Columns have been deleted'; return 'Columns have been deleted';
} }
return null; return null;
@ -71,7 +73,7 @@ export const SettingsIntegrationDatabaseTablesListCard = ({
...table, ...table,
id: table.name, id: table.name,
updatesText: table.schemaPendingUpdates updatesText: table.schemaPendingUpdates
? getTableUpdatesText(table.schemaPendingUpdates) ? getDistantTableUpdatesText(table.schemaPendingUpdates)
: null, : null,
})), })),
); );

View File

@ -42,7 +42,6 @@ export const useDatabaseConnection = () => {
const { tables } = useGetDatabaseConnectionTables({ const { tables } = useGetDatabaseConnectionTables({
connectionId, connectionId,
skip: !connection, skip: !connection,
refreshData: true,
}); });
return { connection, integration, databaseKey, tables }; return { connection, integration, databaseKey, tables };

View File

@ -9,9 +9,9 @@ import {
RemoteServerType, RemoteServerType,
} from 'src/engine/metadata-modules/remote-server/remote-server.entity'; } from 'src/engine/metadata-modules/remote-server/remote-server.entity';
import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service';
import { DistantTableColumn } from 'src/engine/metadata-modules/remote-server/remote-table/distant-table/types/distant-table-column';
import { DistantTables } from 'src/engine/metadata-modules/remote-server/remote-table/distant-table/types/distant-table'; import { DistantTables } from 'src/engine/metadata-modules/remote-server/remote-table/distant-table/types/distant-table';
import { STRIPE_DISTANT_TABLES } from 'src/engine/metadata-modules/remote-server/remote-table/distant-table/util/stripe-distant-tables.util'; import { STRIPE_DISTANT_TABLES } from 'src/engine/metadata-modules/remote-server/remote-table/distant-table/util/stripe-distant-tables.util';
import { PostgresTableSchemaColumn } from 'src/engine/metadata-modules/remote-server/types/postgres-table-schema-column';
@Injectable() @Injectable()
export class DistantTableService { export class DistantTableService {
@ -26,7 +26,7 @@ export class DistantTableService {
public getDistantTableColumns( public getDistantTableColumns(
remoteServer: RemoteServerEntity<RemoteServerType>, remoteServer: RemoteServerEntity<RemoteServerType>,
tableName: string, tableName: string,
): DistantTableColumn[] { ): PostgresTableSchemaColumn[] {
if (!remoteServer.availableTables) { if (!remoteServer.availableTables) {
throw new BadRequestException( throw new BadRequestException(
'Remote server available tables are not defined', 'Remote server available tables are not defined',
@ -39,13 +39,8 @@ export class DistantTableService {
public async fetchDistantTables( public async fetchDistantTables(
remoteServer: RemoteServerEntity<RemoteServerType>, remoteServer: RemoteServerEntity<RemoteServerType>,
workspaceId: string, workspaceId: string,
refreshData?: boolean,
): Promise<DistantTables> { ): Promise<DistantTables> {
if (!refreshData && remoteServer.availableTables) { return this.createAvailableTables(remoteServer, workspaceId);
return remoteServer.availableTables;
}
return await this.createAvailableTables(remoteServer, workspaceId);
} }
private async createAvailableTables( private async createAvailableTables(

View File

@ -1,6 +0,0 @@
// Type will evolve as we add more remote table types
export type DistantTableColumn = {
columnName: string;
dataType: string;
udtName: string;
};

View File

@ -1,5 +1,5 @@
import { DistantTableColumn } from 'src/engine/metadata-modules/remote-server/remote-table/distant-table/types/distant-table-column'; import { PostgresTableSchemaColumn } from 'src/engine/metadata-modules/remote-server/types/postgres-table-schema-column';
export type DistantTables = { export type DistantTables = {
[tableName: string]: DistantTableColumn[]; [tableName: string]: PostgresTableSchemaColumn[];
}; };

View File

@ -1,17 +1,9 @@
import { InputType, ID, Field } from '@nestjs/graphql'; import { InputType, ID } from '@nestjs/graphql';
import { IDField } from '@ptc-org/nestjs-query-graphql'; import { IDField } from '@ptc-org/nestjs-query-graphql';
import { IsOptional } from 'class-validator';
@InputType() @InputType()
export class FindManyRemoteTablesInput { export class FindManyRemoteTablesInput {
@IDField(() => ID, { description: 'The id of the remote server.' }) @IDField(() => ID, { description: 'The id of the remote server.' })
id!: string; id!: string;
@IsOptional()
@Field(() => Boolean, {
description: 'Indicates if data from distant tables should be refreshed.',
nullable: true,
})
refreshData?: boolean;
} }

View File

@ -10,11 +10,11 @@ export enum RemoteTableStatus {
NOT_SYNCED = 'NOT_SYNCED', NOT_SYNCED = 'NOT_SYNCED',
} }
export enum TableUpdate { export enum DistantTableUpdate {
TABLE_DELETED = 'TABLE_DELETED', TABLE_DELETED = 'TABLE_DELETED',
COLUMNS_DELETED = 'COLUMN_DELETED', COLUMNS_DELETED = 'COLUMNS_DELETED',
COLUMNS_ADDED = 'COLUMN_ADDED', COLUMNS_ADDED = 'COLUMNS_ADDED',
COLUMNS_TYPE_CHANGED = 'COLUMN_TYPE_CHANGED', COLUMNS_TYPE_CHANGED = 'COLUMNS_TYPE_CHANGED',
} }
registerEnumType(RemoteTableStatus, { registerEnumType(RemoteTableStatus, {
@ -22,8 +22,8 @@ registerEnumType(RemoteTableStatus, {
description: 'Status of the table', description: 'Status of the table',
}); });
registerEnumType(TableUpdate, { registerEnumType(DistantTableUpdate, {
name: 'TableUpdate', name: 'DistantTableUpdate',
description: 'Schema update on a table', description: 'Schema update on a table',
}); });
@ -43,6 +43,6 @@ export class RemoteTableDTO {
schema?: string; schema?: string;
@IsOptional() @IsOptional()
@Field(() => [TableUpdate], { nullable: true }) @Field(() => [DistantTableUpdate], { nullable: true })
schemaPendingUpdates?: [TableUpdate]; schemaPendingUpdates?: [DistantTableUpdate];
} }

View File

@ -22,7 +22,6 @@ export class RemoteTableResolver {
return this.remoteTableService.findDistantTablesByServerId( return this.remoteTableService.findDistantTablesByServerId(
input.id, input.id,
workspaceId, workspaceId,
input.refreshData,
); );
} }

View File

@ -10,7 +10,7 @@ import {
} from 'src/engine/metadata-modules/remote-server/remote-server.entity'; } from 'src/engine/metadata-modules/remote-server/remote-server.entity';
import { import {
RemoteTableStatus, RemoteTableStatus,
TableUpdate, DistantTableUpdate,
} from 'src/engine/metadata-modules/remote-server/remote-table/dtos/remote-table.dto'; } from 'src/engine/metadata-modules/remote-server/remote-table/dtos/remote-table.dto';
import { import {
mapUdtNameToFieldType, mapUdtNameToFieldType,
@ -38,9 +38,9 @@ import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/work
import { RemoteTableEntity } from 'src/engine/metadata-modules/remote-server/remote-table/remote-table.entity'; import { RemoteTableEntity } from 'src/engine/metadata-modules/remote-server/remote-table/remote-table.entity';
import { getRemoteTableLocalName } from 'src/engine/metadata-modules/remote-server/remote-table/utils/get-remote-table-local-name.util'; import { getRemoteTableLocalName } from 'src/engine/metadata-modules/remote-server/remote-table/utils/get-remote-table-local-name.util';
import { DistantTableService } from 'src/engine/metadata-modules/remote-server/remote-table/distant-table/distant-table.service'; import { DistantTableService } from 'src/engine/metadata-modules/remote-server/remote-table/distant-table/distant-table.service';
import { DistantTableColumn } from 'src/engine/metadata-modules/remote-server/remote-table/distant-table/types/distant-table-column';
import { DistantTables } from 'src/engine/metadata-modules/remote-server/remote-table/distant-table/types/distant-table'; import { DistantTables } from 'src/engine/metadata-modules/remote-server/remote-table/distant-table/types/distant-table';
import { RemoteTableSchemaColumn } from 'src/engine/metadata-modules/remote-server/remote-table/types/remote-table-schema-column'; import { getForeignTableColumnName } from 'src/engine/metadata-modules/remote-server/remote-table/utils/get-foreign-table-column-name.util';
import { PostgresTableSchemaColumn } from 'src/engine/metadata-modules/remote-server/types/postgres-table-schema-column';
export class RemoteTableService { export class RemoteTableService {
private readonly logger = new Logger(RemoteTableService.name); private readonly logger = new Logger(RemoteTableService.name);
@ -62,11 +62,7 @@ export class RemoteTableService {
private readonly workspaceDataSourceService: WorkspaceDataSourceService, private readonly workspaceDataSourceService: WorkspaceDataSourceService,
) {} ) {}
public async findDistantTablesByServerId( public async findDistantTablesByServerId(id: string, workspaceId: string) {
id: string,
workspaceId: string,
refreshData?: boolean,
) {
const remoteServer = await this.remoteServerRepository.findOne({ const remoteServer = await this.remoteServerRepository.findOne({
where: { where: {
id, id,
@ -90,10 +86,9 @@ export class RemoteTableService {
const distantTables = await this.distantTableService.fetchDistantTables( const distantTables = await this.distantTableService.fetchDistantTables(
remoteServer, remoteServer,
workspaceId, workspaceId,
refreshData,
); );
if (!refreshData || currentRemoteTables.length === 0) { if (currentRemoteTables.length === 0) {
const distantTablesWithStatus = Object.keys(distantTables).map( const distantTablesWithStatus = Object.keys(distantTables).map(
(tableName) => ({ (tableName) => ({
name: tableName, name: tableName,
@ -107,18 +102,41 @@ export class RemoteTableService {
return distantTablesWithStatus; return distantTablesWithStatus;
} }
return this.getDistantTablesWithUpdates({
remoteServerSchema: remoteServer.schema,
workspaceId,
remoteTables: currentRemoteTables,
distantTables,
});
}
private async getDistantTablesWithUpdates({
remoteServerSchema,
workspaceId,
remoteTables,
distantTables,
}: {
remoteServerSchema: string;
workspaceId: string;
remoteTables: RemoteTableEntity[];
distantTables: DistantTables;
}) {
const schemaPendingUpdates = const schemaPendingUpdates =
await this.getSchemaUpdatesBetweenRemoteAndDistantTables({ await this.getSchemaUpdatesBetweenForeignAndDistantTables({
workspaceId, workspaceId,
remoteTables: currentRemoteTables, remoteTables,
distantTables, distantTables,
}); });
const remoteTablesDistantNames = remoteTables.map(
(remoteTable) => remoteTable.distantTableName,
);
const distantTablesWithUpdates = Object.keys(distantTables).map( const distantTablesWithUpdates = Object.keys(distantTables).map(
(tableName) => ({ (tableName) => ({
name: tableName, name: tableName,
schema: remoteServer.schema, schema: remoteServerSchema,
status: currentRemoteTableDistantNames.includes(tableName) status: remoteTablesDistantNames.includes(tableName)
? RemoteTableStatus.SYNCED ? RemoteTableStatus.SYNCED
: RemoteTableStatus.NOT_SYNCED, : RemoteTableStatus.NOT_SYNCED,
schemaPendingUpdates: schemaPendingUpdates[tableName], schemaPendingUpdates: schemaPendingUpdates[tableName],
@ -127,11 +145,11 @@ export class RemoteTableService {
const deletedTables = Object.entries(schemaPendingUpdates) const deletedTables = Object.entries(schemaPendingUpdates)
.filter(([_tableName, updates]) => .filter(([_tableName, updates]) =>
updates.includes(TableUpdate.TABLE_DELETED), updates.includes(DistantTableUpdate.TABLE_DELETED),
) )
.map(([tableName, updates]) => ({ .map(([tableName, updates]) => ({
name: tableName, name: tableName,
schema: remoteServer.schema, schema: remoteServerSchema,
status: RemoteTableStatus.SYNCED, status: RemoteTableStatus.SYNCED,
schemaPendingUpdates: updates, schemaPendingUpdates: updates,
})); }));
@ -139,7 +157,7 @@ export class RemoteTableService {
return distantTablesWithUpdates.concat(deletedTables); return distantTablesWithUpdates.concat(deletedTables);
} }
private async getSchemaUpdatesBetweenRemoteAndDistantTables({ private async getSchemaUpdatesBetweenForeignAndDistantTables({
workspaceId, workspaceId,
remoteTables, remoteTables,
distantTables, distantTables,
@ -147,7 +165,7 @@ export class RemoteTableService {
workspaceId: string; workspaceId: string;
remoteTables: RemoteTableEntity[]; remoteTables: RemoteTableEntity[];
distantTables: DistantTables; distantTables: DistantTables;
}): Promise<{ [tablename: string]: TableUpdate[] }> { }): Promise<{ [tablename: string]: DistantTableUpdate[] }> {
const updates = {}; const updates = {};
for (const remoteTable of remoteTables) { for (const remoteTable of remoteTables) {
@ -155,37 +173,39 @@ export class RemoteTableService {
const tableName = remoteTable.distantTableName; const tableName = remoteTable.distantTableName;
if (!distantTable) { if (!distantTable) {
updates[tableName] = [TableUpdate.TABLE_DELETED]; updates[tableName] = [DistantTableUpdate.TABLE_DELETED];
continue; continue;
} }
const distantColumnNames = new Set( const distantTableColumnNames = new Set(
distantTable.map((column) => column.columnName), distantTable.map((column) =>
getForeignTableColumnName(column.columnName),
),
); );
const localColumnNames = new Set( const foreignTableColumnNames = new Set(
( (
await this.fetchTableColumns(workspaceId, remoteTable.localTableName) await this.fetchTableColumns(workspaceId, remoteTable.localTableName)
).map((column) => column.column_name), ).map((column) => column.columnName),
); );
const columnsAdded = [...distantColumnNames].filter( const columnsAdded = [...distantTableColumnNames].filter(
(columnName) => !localColumnNames.has(columnName), (columnName) => !foreignTableColumnNames.has(columnName),
); );
const columnsDeleted = [...localColumnNames].filter( const columnsDeleted = [...foreignTableColumnNames].filter(
(columnName) => !distantColumnNames.has(columnName), (columnName) => !distantTableColumnNames.has(columnName),
); );
if (columnsAdded.length > 0) { if (columnsAdded.length > 0) {
updates[tableName] = [ updates[tableName] = [
...(updates[tableName] || []), ...(updates[tableName] || []),
TableUpdate.COLUMNS_ADDED, DistantTableUpdate.COLUMNS_ADDED,
]; ];
} }
if (columnsDeleted.length > 0) { if (columnsDeleted.length > 0) {
updates[tableName] = [ updates[tableName] = [
...(updates[tableName] || []), ...(updates[tableName] || []),
TableUpdate.COLUMNS_DELETED, DistantTableUpdate.COLUMNS_DELETED,
]; ];
} }
} }
@ -431,7 +451,7 @@ export class RemoteTableService {
private async fetchTableColumns( private async fetchTableColumns(
workspaceId: string, workspaceId: string,
tableName: string, tableName: string,
): Promise<RemoteTableSchemaColumn[]> { ): Promise<PostgresTableSchemaColumn[]> {
const workspaceDataSource = const workspaceDataSource =
await this.workspaceDataSourceService.connectToWorkspaceDataSource( await this.workspaceDataSourceService.connectToWorkspaceDataSource(
workspaceId, workspaceId,
@ -440,11 +460,17 @@ export class RemoteTableService {
const schemaName = const schemaName =
this.workspaceDataSourceService.getSchemaName(workspaceId); this.workspaceDataSourceService.getSchemaName(workspaceId);
return await workspaceDataSource.query( const res = await workspaceDataSource.query(
`SELECT column_name, data_type `SELECT column_name, data_type, udt_name
FROM information_schema.columns FROM information_schema.columns
WHERE table_schema = '${schemaName}' AND table_name = '${tableName}'`, WHERE table_schema = '${schemaName}' AND table_name = '${tableName}'`,
); );
return res.map((column) => ({
columnName: column.column_name,
dataType: column.data_type,
udtName: column.udt_name,
}));
} }
private async createForeignTable( private async createForeignTable(
@ -452,7 +478,7 @@ export class RemoteTableService {
localTableName: string, localTableName: string,
remoteTableInput: RemoteTableInput, remoteTableInput: RemoteTableInput,
remoteServer: RemoteServerEntity<RemoteServerType>, remoteServer: RemoteServerEntity<RemoteServerType>,
distantTableColumns: DistantTableColumn[], distantTableColumns: PostgresTableSchemaColumn[],
) { ) {
const referencedTable: ReferencedTable = this.buildReferencedTable( const referencedTable: ReferencedTable = this.buildReferencedTable(
remoteServer, remoteServer,
@ -471,7 +497,7 @@ export class RemoteTableService {
columns: distantTableColumns.map( columns: distantTableColumns.map(
(column) => (column) =>
({ ({
columnName: camelCase(column.columnName), columnName: getForeignTableColumnName(column.columnName),
columnType: column.dataType, columnType: column.dataType,
distantColumnName: column.columnName, distantColumnName: column.columnName,
}) satisfies WorkspaceMigrationForeignColumnDefinition, }) satisfies WorkspaceMigrationForeignColumnDefinition,
@ -500,8 +526,8 @@ export class RemoteTableService {
private async createRemoteTableMetadata( private async createRemoteTableMetadata(
workspaceId: string, workspaceId: string,
localTableName: string, localTableName: string,
distantTableColumns: DistantTableColumn[], distantTableColumns: PostgresTableSchemaColumn[],
distantTableIdColumn: DistantTableColumn, distantTableIdColumn: PostgresTableSchemaColumn,
dataSourceMetadataId: string, dataSourceMetadataId: string,
) { ) {
const objectMetadata = await this.objectMetadataService.createOne({ const objectMetadata = await this.objectMetadataService.createOne({

View File

@ -1,4 +0,0 @@
export type RemoteTableSchemaColumn = {
column_name: string;
data_type: string;
};

View File

@ -0,0 +1,5 @@
import camelCase from 'lodash.camelcase';
export const getForeignTableColumnName = (distantTableColumnName: string) => {
return camelCase(distantTableColumnName);
};

View File

@ -0,0 +1,5 @@
export type PostgresTableSchemaColumn = {
columnName: string;
dataType: string;
udtName: string;
};