Skip to content

Commit 3deda2f

Browse files
authored
Update foreign table to distant table schema (#5508)
Closes #5069 back-end part And: - do not display schemaPendingUpdates status on remote server lists as this call will become too costly if there are dozens of servers - (refacto) create foreignTableService After this is merged we will be able to delete remoteTable's availableTables column
1 parent 29c2780 commit 3deda2f

21 files changed

+618
-296
lines changed

packages/twenty-front/src/generated-metadata/graphql.ts

+2
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,8 @@ export enum FileFolder {
338338
export type FindManyRemoteTablesInput = {
339339
/** The id of the remote server. */
340340
id: Scalars['ID']['input'];
341+
/** Indicates if pending schema updates status should be computed. */
342+
shouldFetchPendingSchemaUpdates?: InputMaybe<Scalars['Boolean']['input']>;
341343
};
342344

343345
export type FullName = {

packages/twenty-front/src/modules/databases/hooks/useGetDatabaseConnectionTables.ts

+3
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ import {
1010
type UseGetDatabaseConnectionTablesParams = {
1111
connectionId: string;
1212
skip?: boolean;
13+
shouldFetchPendingSchemaUpdates?: boolean;
1314
};
1415

1516
export const useGetDatabaseConnectionTables = ({
1617
connectionId,
1718
skip,
19+
shouldFetchPendingSchemaUpdates,
1820
}: UseGetDatabaseConnectionTablesParams) => {
1921
const apolloMetadataClient = useApolloMetadataClient();
2022

@@ -27,6 +29,7 @@ export const useGetDatabaseConnectionTables = ({
2729
variables: {
2830
input: {
2931
id: connectionId,
32+
shouldFetchPendingSchemaUpdates,
3033
},
3134
},
3235
});

packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionSummaryCard.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export const SettingsIntegrationDatabaseConnectionSummaryCard = ({
5353
<>
5454
<SettingsIntegrationDatabaseConnectionSyncStatus
5555
connectionId={connectionId}
56+
shouldFetchPendingSchemaUpdates
5657
/>
5758
<Dropdown
5859
dropdownId={dropdownId}

packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionSyncStatus.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,18 @@ import { isDefined } from '~/utils/isDefined';
66
type SettingsIntegrationDatabaseConnectionSyncStatusProps = {
77
connectionId: string;
88
skip?: boolean;
9+
shouldFetchPendingSchemaUpdates?: boolean;
910
};
1011

1112
export const SettingsIntegrationDatabaseConnectionSyncStatus = ({
1213
connectionId,
1314
skip,
15+
shouldFetchPendingSchemaUpdates,
1416
}: SettingsIntegrationDatabaseConnectionSyncStatusProps) => {
1517
const { tables, error } = useGetDatabaseConnectionTables({
1618
connectionId,
1719
skip,
20+
shouldFetchPendingSchemaUpdates,
1821
});
1922

2023
if (isDefined(error)) {

packages/twenty-front/src/modules/settings/integrations/database-connection/hooks/useDatabaseConnection.ts

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export const useDatabaseConnection = () => {
4242
const { tables } = useGetDatabaseConnectionTables({
4343
connectionId,
4444
skip: !connection,
45+
shouldFetchPendingSchemaUpdates: true,
4546
});
4647

4748
return { connection, integration, databaseKey, tables };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { MigrationInterface, QueryRunner } from 'typeorm';
2+
3+
export class RemoveAvailableTables1716310822694 implements MigrationInterface {
4+
name = 'RemoveAvailableTables1716310822694';
5+
6+
public async up(queryRunner: QueryRunner): Promise<void> {
7+
await queryRunner.query(
8+
`ALTER TABLE "metadata"."remoteServer" DROP COLUMN "availableTables"`,
9+
);
10+
}
11+
12+
public async down(queryRunner: QueryRunner): Promise<void> {
13+
await queryRunner.query(
14+
`ALTER TABLE "metadata"."remoteServer" ADD "availableTables" jsonb`,
15+
);
16+
}
17+
}

packages/twenty-server/src/engine/metadata-modules/remote-server/remote-server.entity.ts

-4
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import {
1111

1212
import { RemoteTableEntity } from 'src/engine/metadata-modules/remote-server/remote-table/remote-table.entity';
1313
import { UserMappingOptions } from 'src/engine/metadata-modules/remote-server/types/user-mapping-options';
14-
import { DistantTables } from 'src/engine/metadata-modules/remote-server/remote-table/distant-table/types/distant-table';
1514

1615
export enum RemoteServerType {
1716
POSTGRES_FDW = 'postgres_fdw',
@@ -59,9 +58,6 @@ export class RemoteServerEntity<T extends RemoteServerType> {
5958
@Column({ nullable: false, type: 'uuid' })
6059
workspaceId: string;
6160

62-
@Column({ type: 'jsonb', nullable: true })
63-
availableTables: DistantTables;
64-
6561
@OneToMany(() => RemoteTableEntity, (table) => table.server, {
6662
cascade: true,
6763
})

packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/distant-table/distant-table.service.ts

+28-35
Original file line numberDiff line numberDiff line change
@@ -23,43 +23,40 @@ export class DistantTableService {
2323
>,
2424
) {}
2525

26-
public getDistantTableColumns(
27-
remoteServer: RemoteServerEntity<RemoteServerType>,
28-
tableName: string,
29-
): PostgresTableSchemaColumn[] {
30-
if (!remoteServer.availableTables) {
31-
throw new BadRequestException(
32-
'Remote server available tables are not defined',
33-
);
34-
}
35-
36-
return remoteServer.availableTables[tableName];
37-
}
38-
3926
public async fetchDistantTables(
4027
remoteServer: RemoteServerEntity<RemoteServerType>,
4128
workspaceId: string,
42-
): Promise<DistantTables> {
43-
return this.createAvailableTables(remoteServer, workspaceId);
44-
}
45-
46-
private async createAvailableTables(
47-
remoteServer: RemoteServerEntity<RemoteServerType>,
48-
workspaceId: string,
29+
tableName?: string,
4930
): Promise<DistantTables> {
5031
if (remoteServer.schema) {
51-
return this.createAvailableTablesFromDynamicSchema(
32+
return this.getDistantTablesFromDynamicSchema(
5233
remoteServer,
5334
workspaceId,
35+
tableName,
5436
);
5537
}
5638

57-
return this.createAvailableTablesFromStaticSchema(remoteServer);
39+
return this.getDistantTablesFromStaticSchema(remoteServer);
5840
}
5941

60-
private async createAvailableTablesFromDynamicSchema(
42+
public async getDistantTableColumns(
6143
remoteServer: RemoteServerEntity<RemoteServerType>,
6244
workspaceId: string,
45+
tableName: string,
46+
): Promise<PostgresTableSchemaColumn[]> {
47+
const distantTables = await this.fetchDistantTables(
48+
remoteServer,
49+
workspaceId,
50+
tableName,
51+
);
52+
53+
return distantTables[tableName] || [];
54+
}
55+
56+
private async getDistantTablesFromDynamicSchema(
57+
remoteServer: RemoteServerEntity<RemoteServerType>,
58+
workspaceId: string,
59+
tableName?: string,
6360
): Promise<DistantTables> {
6461
if (!remoteServer.schema) {
6562
throw new BadRequestException('Remote server schema is not defined');
@@ -73,12 +70,16 @@ export class DistantTableService {
7370
workspaceId,
7471
);
7572

76-
const availableTables = await workspaceDataSource.transaction(
73+
const distantTables = await workspaceDataSource.transaction(
7774
async (entityManager: EntityManager) => {
7875
await entityManager.query(`CREATE SCHEMA "${tmpSchemaName}"`);
7976

77+
const tableLimitationsOptions = tableName
78+
? ` LIMIT TO (${tableName})`
79+
: '';
80+
8081
await entityManager.query(
81-
`IMPORT FOREIGN SCHEMA "${remoteServer.schema}" FROM SERVER "${remoteServer.foreignDataWrapperId}" INTO "${tmpSchemaName}"`,
82+
`IMPORT FOREIGN SCHEMA "${remoteServer.schema}"${tableLimitationsOptions} FROM SERVER "${remoteServer.foreignDataWrapperId}" INTO "${tmpSchemaName}"`,
8283
);
8384

8485
const createdForeignTableNames = await entityManager.query(
@@ -106,22 +107,14 @@ export class DistantTableService {
106107
},
107108
);
108109

109-
await this.remoteServerRepository.update(remoteServer.id, {
110-
availableTables,
111-
});
112-
113-
return availableTables;
110+
return distantTables;
114111
}
115112

116-
private async createAvailableTablesFromStaticSchema(
113+
private async getDistantTablesFromStaticSchema(
117114
remoteServer: RemoteServerEntity<RemoteServerType>,
118115
): Promise<DistantTables> {
119116
switch (remoteServer.foreignDataWrapperType) {
120117
case RemoteServerType.STRIPE_FDW:
121-
this.remoteServerRepository.update(remoteServer.id, {
122-
availableTables: STRIPE_DISTANT_TABLES,
123-
});
124-
125118
return STRIPE_DISTANT_TABLES;
126119
default:
127120
throw new BadRequestException(
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { PostgresTableSchemaColumn } from 'src/engine/metadata-modules/remote-server/types/postgres-table-schema-column';
22

33
export type DistantTables = {
4-
[tableName: string]: PostgresTableSchemaColumn[];
4+
[distantTableName: string]: PostgresTableSchemaColumn[];
55
};
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
1-
import { InputType, ID } from '@nestjs/graphql';
1+
import { InputType, ID, Field } from '@nestjs/graphql';
22

33
import { IDField } from '@ptc-org/nestjs-query-graphql';
4+
import { IsOptional } from 'class-validator';
45

56
@InputType()
67
export class FindManyRemoteTablesInput {
78
@IDField(() => ID, { description: 'The id of the remote server.' })
89
id!: string;
10+
11+
@IsOptional()
12+
@Field(() => Boolean, {
13+
description:
14+
'Indicates if pending schema updates status should be computed.',
15+
nullable: true,
16+
})
17+
shouldFetchPendingSchemaUpdates?: boolean;
918
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Module } from '@nestjs/common';
2+
3+
import { ForeignTableService } from 'src/engine/metadata-modules/remote-server/remote-table/foreign-table/foreign-table.service';
4+
import { WorkspaceCacheVersionModule } from 'src/engine/metadata-modules/workspace-cache-version/workspace-cache-version.module';
5+
import { WorkspaceMigrationModule } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.module';
6+
import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module';
7+
import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.module';
8+
9+
@Module({
10+
imports: [
11+
WorkspaceMigrationModule,
12+
WorkspaceMigrationRunnerModule,
13+
WorkspaceDataSourceModule,
14+
WorkspaceCacheVersionModule,
15+
],
16+
providers: [ForeignTableService],
17+
exports: [ForeignTableService],
18+
})
19+
export class ForeignTableModule {}

0 commit comments

Comments
 (0)