From 5bb205bd6aa5d76e6abd79da9c15c2e340ef6770 Mon Sep 17 00:00:00 2001 From: Marie <51697796+ijreilly@users.noreply.github.com> Date: Tue, 28 May 2024 18:01:05 +0200 Subject: [PATCH] Fix update remote field metadata (#5638) Closes #5610. & update fetch-policy when fetching database on the remote databases show page to get freshest status. --- .../hooks/useGetDatabaseConnection.ts | 7 +- .../hooks/useGetDatabaseConnectionTables.ts | 7 +- .../hooks/useSyncRemoteTableSchemaChanges.ts | 22 +++- ...grationDatabaseConnectionShowContainer.tsx | 2 +- ...grationEditDatabaseConnectionContainer.tsx | 2 +- .../hooks/useDatabaseConnection.ts | 9 +- .../distant-table/distant-table.service.ts | 4 +- .../foreign-table/foreign-table.service.ts | 2 +- .../remote-table/remote-table.service.ts | 116 +++++++++++++++--- 9 files changed, 146 insertions(+), 25 deletions(-) diff --git a/packages/twenty-front/src/modules/databases/hooks/useGetDatabaseConnection.ts b/packages/twenty-front/src/modules/databases/hooks/useGetDatabaseConnection.ts index 5d13777dfb8a..ed8ee1cbba52 100644 --- a/packages/twenty-front/src/modules/databases/hooks/useGetDatabaseConnection.ts +++ b/packages/twenty-front/src/modules/databases/hooks/useGetDatabaseConnection.ts @@ -1,4 +1,4 @@ -import { useQuery } from '@apollo/client'; +import { useQuery, WatchQueryFetchPolicy } from '@apollo/client'; import { GET_ONE_DATABASE_CONNECTION } from '@/databases/graphql/queries/findOneDatabaseConnection'; import { getForeignDataWrapperType } from '@/databases/utils/getForeignDataWrapperType'; @@ -12,16 +12,20 @@ type UseGetDatabaseConnectionParams = { databaseKey: string; connectionId: string; skip?: boolean; + fetchPolicy?: WatchQueryFetchPolicy; }; export const useGetDatabaseConnection = ({ databaseKey, connectionId, skip, + fetchPolicy, }: UseGetDatabaseConnectionParams) => { const apolloMetadataClient = useApolloMetadataClient(); const foreignDataWrapperType = getForeignDataWrapperType(databaseKey); + const fetchPolicyOption = fetchPolicy ? { fetchPolicy: fetchPolicy } : {}; + const { data, loading } = useQuery< GetOneDatabaseConnectionQuery, GetOneDatabaseConnectionQueryVariables @@ -33,6 +37,7 @@ export const useGetDatabaseConnection = ({ id: connectionId, }, }, + ...fetchPolicyOption, }); const connection = data?.findOneRemoteServerById ?? null; diff --git a/packages/twenty-front/src/modules/databases/hooks/useGetDatabaseConnectionTables.ts b/packages/twenty-front/src/modules/databases/hooks/useGetDatabaseConnectionTables.ts index 80f8f3a1e256..b5da7e1eadcd 100644 --- a/packages/twenty-front/src/modules/databases/hooks/useGetDatabaseConnectionTables.ts +++ b/packages/twenty-front/src/modules/databases/hooks/useGetDatabaseConnectionTables.ts @@ -1,4 +1,4 @@ -import { useQuery } from '@apollo/client'; +import { useQuery, WatchQueryFetchPolicy } from '@apollo/client'; import { GET_MANY_REMOTE_TABLES } from '@/databases/graphql/queries/findManyRemoteTables'; import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetadataClient'; @@ -11,15 +11,19 @@ type UseGetDatabaseConnectionTablesParams = { connectionId: string; skip?: boolean; shouldFetchPendingSchemaUpdates?: boolean; + fetchPolicy?: WatchQueryFetchPolicy; }; export const useGetDatabaseConnectionTables = ({ connectionId, skip, shouldFetchPendingSchemaUpdates, + fetchPolicy, }: UseGetDatabaseConnectionTablesParams) => { const apolloMetadataClient = useApolloMetadataClient(); + const fetchPolicyOption = fetchPolicy ? { fetchPolicy: fetchPolicy } : {}; + const { data, error } = useQuery< GetManyRemoteTablesQuery, GetManyRemoteTablesQueryVariables @@ -32,6 +36,7 @@ export const useGetDatabaseConnectionTables = ({ shouldFetchPendingSchemaUpdates, }, }, + ...fetchPolicyOption, }); return { diff --git a/packages/twenty-front/src/modules/databases/hooks/useSyncRemoteTableSchemaChanges.ts b/packages/twenty-front/src/modules/databases/hooks/useSyncRemoteTableSchemaChanges.ts index 4f1536ee1709..a352eb0b77e4 100644 --- a/packages/twenty-front/src/modules/databases/hooks/useSyncRemoteTableSchemaChanges.ts +++ b/packages/twenty-front/src/modules/databases/hooks/useSyncRemoteTableSchemaChanges.ts @@ -1,9 +1,12 @@ import { useCallback } from 'react'; -import { ApolloClient, useMutation } from '@apollo/client'; +import { ApolloClient, useApolloClient, useMutation } from '@apollo/client'; import { SYNC_REMOTE_TABLE_SCHEMA_CHANGES } from '@/databases/graphql/mutations/syncRemoteTableSchemaChanges'; import { modifyRemoteTableFromCache } from '@/databases/utils/modifyRemoteTableFromCache'; import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetadataClient'; +import { useFindManyObjectMetadataItems } from '@/object-metadata/hooks/useFindManyObjectMetadataItems'; +import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { useFindManyRecordsQuery } from '@/object-record/hooks/useFindManyRecordsQuery'; import { RemoteTableInput, SyncRemoteTableSchemaChangesMutation, @@ -13,6 +16,14 @@ import { isDefined } from '~/utils/isDefined'; export const useSyncRemoteTableSchemaChanges = () => { const apolloMetadataClient = useApolloMetadataClient(); + const apolloClient = useApolloClient(); + + const { refetch: refetchObjectMetadataItems } = + useFindManyObjectMetadataItems(); + + const { findManyRecordsQuery: findManyViewsQuery } = useFindManyRecordsQuery({ + objectNameSingular: CoreObjectNameSingular.View, + }); const [mutate, mutationInformation] = useMutation< SyncRemoteTableSchemaChangesMutation, @@ -42,9 +53,16 @@ export const useSyncRemoteTableSchemaChanges = () => { }, }); + await refetchObjectMetadataItems(); + + await apolloClient.query({ + query: findManyViewsQuery, + fetchPolicy: 'network-only', + }); + return remoteTable; }, - [mutate], + [mutate, refetchObjectMetadataItems, findManyViewsQuery, apolloClient], ); return { diff --git a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionShowContainer.tsx b/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionShowContainer.tsx index ffc175816b1e..b4317ee930fe 100644 --- a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionShowContainer.tsx +++ b/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionShowContainer.tsx @@ -13,7 +13,7 @@ import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb'; export const SettingsIntegrationDatabaseConnectionShowContainer = () => { const navigate = useNavigate(); const { connection, integration, databaseKey, tables } = - useDatabaseConnection(); + useDatabaseConnection({ fetchPolicy: 'network-only' }); const { deleteOneDatabaseConnection } = useDeleteOneDatabaseConnection(); diff --git a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationEditDatabaseConnectionContainer.tsx b/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationEditDatabaseConnectionContainer.tsx index fffde050edca..85c783926999 100644 --- a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationEditDatabaseConnectionContainer.tsx +++ b/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationEditDatabaseConnectionContainer.tsx @@ -3,7 +3,7 @@ import { useDatabaseConnection } from '@/settings/integrations/database-connecti export const SettingsIntegrationEditDatabaseConnectionContainer = () => { const { connection, integration, databaseKey, tables } = - useDatabaseConnection(); + useDatabaseConnection({}); if (!connection || !integration) return null; diff --git a/packages/twenty-front/src/modules/settings/integrations/database-connection/hooks/useDatabaseConnection.ts b/packages/twenty-front/src/modules/settings/integrations/database-connection/hooks/useDatabaseConnection.ts index 0079630765e0..3353c39dda40 100644 --- a/packages/twenty-front/src/modules/settings/integrations/database-connection/hooks/useDatabaseConnection.ts +++ b/packages/twenty-front/src/modules/settings/integrations/database-connection/hooks/useDatabaseConnection.ts @@ -1,5 +1,6 @@ import { useEffect } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; +import { WatchQueryFetchPolicy } from '@apollo/client'; import { useGetDatabaseConnection } from '@/databases/hooks/useGetDatabaseConnection'; import { useGetDatabaseConnectionTables } from '@/databases/hooks/useGetDatabaseConnectionTables'; @@ -7,7 +8,11 @@ import { useIsSettingsIntegrationEnabled } from '@/settings/integrations/hooks/u import { useSettingsIntegrationCategories } from '@/settings/integrations/hooks/useSettingsIntegrationCategories'; import { AppPath } from '@/types/AppPath'; -export const useDatabaseConnection = () => { +export const useDatabaseConnection = ({ + fetchPolicy, +}: { + fetchPolicy?: WatchQueryFetchPolicy; +}) => { const { databaseKey = '', connectionId = '' } = useParams(); const navigate = useNavigate(); @@ -24,6 +29,7 @@ export const useDatabaseConnection = () => { databaseKey, connectionId, skip: !isIntegrationAvailable, + fetchPolicy, }); useEffect(() => { @@ -43,6 +49,7 @@ export const useDatabaseConnection = () => { connectionId, skip: !connection, shouldFetchPendingSchemaUpdates: true, + fetchPolicy, }); return { connection, integration, databaseKey, tables }; diff --git a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/distant-table/distant-table.service.ts b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/distant-table/distant-table.service.ts index 771c6db4f54f..64d15785ae7b 100644 --- a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/distant-table/distant-table.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/distant-table/distant-table.service.ts @@ -110,9 +110,9 @@ export class DistantTableService { return distantTables; } - private async getDistantTablesFromStaticSchema( + private getDistantTablesFromStaticSchema( remoteServer: RemoteServerEntity, - ): Promise { + ): DistantTables { switch (remoteServer.foreignDataWrapperType) { case RemoteServerType.STRIPE_FDW: return STRIPE_DISTANT_TABLES; diff --git a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/foreign-table/foreign-table.service.ts b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/foreign-table/foreign-table.service.ts index d660626dcd07..c5951b69b173 100644 --- a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/foreign-table/foreign-table.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/foreign-table/foreign-table.service.ts @@ -99,7 +99,7 @@ export class ForeignTableService { public async updateForeignTable( foreignTableName: string, workspaceId: string, - columnsUpdates?: WorkspaceMigrationColumnAction[], + columnsUpdates: WorkspaceMigrationColumnAction[], ) { const workspaceMigration = await this.workspaceMigrationService.createCustomMigration( 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 e05f5accd544..732108af8330 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 @@ -14,15 +14,14 @@ import { RemoteTableStatus, } from 'src/engine/metadata-modules/remote-server/remote-table/dtos/remote-table.dto'; import { - mapUdtNameToFieldType, mapUdtNameToFieldSettings, + mapUdtNameToFieldType, } from 'src/engine/metadata-modules/remote-server/remote-table/utils/udt-name-mapper.util'; import { RemoteTableInput } from 'src/engine/metadata-modules/remote-server/remote-table/dtos/remote-table-input'; import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; import { ObjectMetadataService } from 'src/engine/metadata-modules/object-metadata/object-metadata.service'; import { CreateObjectInput } from 'src/engine/metadata-modules/object-metadata/dtos/create-object.input'; import { FieldMetadataService } from 'src/engine/metadata-modules/field-metadata/field-metadata.service'; -import { CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input'; import { WorkspaceCacheVersionService } from 'src/engine/metadata-modules/workspace-cache-version/workspace-cache-version.service'; import { camelCase } from 'src/utils/camel-case'; import { camelToTitleCase } from 'src/utils/camel-to-title-case'; @@ -35,6 +34,12 @@ import { fetchTableColumns } from 'src/engine/metadata-modules/remote-server/rem import { ForeignTableService } from 'src/engine/metadata-modules/remote-server/remote-table/foreign-table/foreign-table.service'; import { RemoteTableSchemaUpdateService } from 'src/engine/metadata-modules/remote-server/remote-table/remote-table-schema-update/remote-table-schema-update.service'; import { sortDistantTables } from 'src/engine/metadata-modules/remote-server/remote-table/distant-table/utils/sort-distant-tables.util'; +import { + WorkspaceMigrationColumnAction, + WorkspaceMigrationColumnActionType, +} from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity'; +import { CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input'; +import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; export class RemoteTableService { private readonly logger = new Logger(RemoteTableService.name); @@ -353,7 +358,7 @@ export class RemoteTableService { }; } - const updatedTable = await this.foreignTableService.updateForeignTable( + const updatedTable = await this.updateForeignTableAndFieldsMetadata( remoteTable.localTableName, workspaceId, columnsUpdates, @@ -436,18 +441,12 @@ export class RemoteTableService { // TODO: return error to the user when a column cannot be managed try { - const field = await this.fieldMetadataService.createOne({ - name: columnName, - label: camelToTitleCase(columnName), - description: 'Field of remote', - type: mapUdtNameToFieldType(column.udtName), - workspaceId: workspaceId, - objectMetadataId: objectMetadata.id, - isRemoteCreation: true, - isNullable: true, - icon: 'IconPlug', - settings: mapUdtNameToFieldSettings(column.udtName), - } satisfies CreateFieldInput); + const field = await this.createFieldMetadataForForeignTableColumn( + workspaceId, + columnName, + column.udtName, + objectMetadata.id, + ); if (columnName === 'id') { await this.objectMetadataService.updateOne(objectMetadata.id, { @@ -489,4 +488,91 @@ export class RemoteTableService { return [...distantTablesWithUpdates, ...deletedTables]; } + + private async updateForeignTableAndFieldsMetadata( + foreignTableName: string, + workspaceId: string, + columnsUpdates: WorkspaceMigrationColumnAction[], + ) { + const updatedForeignTable = + await this.foreignTableService.updateForeignTable( + foreignTableName, + workspaceId, + columnsUpdates, + ); + + const objectMetadata = + await this.objectMetadataService.findOneWithinWorkspace(workspaceId, { + where: { nameSingular: foreignTableName }, + }); + + if (!objectMetadata) { + throw new NotFoundException( + `Cannot find associated object for table ${foreignTableName}`, + ); + } + for (const columnUpdate of columnsUpdates) { + this.updateFieldMetadataFromColumnUpdate( + columnUpdate, + workspaceId, + objectMetadata.id, + ); + } + + return updatedForeignTable; + } + + private async updateFieldMetadataFromColumnUpdate( + columnUpdate: WorkspaceMigrationColumnAction, + workspaceId: string, + objectMetadataId: string, + ) { + if (columnUpdate.action === WorkspaceMigrationColumnActionType.CREATE) { + await this.createFieldMetadataForForeignTableColumn( + workspaceId, + columnUpdate.columnName, + columnUpdate.columnType, + objectMetadataId, + ); + } + if (columnUpdate.action === WorkspaceMigrationColumnActionType.DROP) { + const columnName = columnUpdate.columnName; + + const fieldMetadataToDelete = + await this.fieldMetadataService.findOneWithinWorkspace(workspaceId, { + where: { + objectMetadataId: objectMetadataId, + name: columnName, + }, + }); + + if (!fieldMetadataToDelete) { + throw new NotFoundException( + `Cannot find associated field metadata for column ${columnName}`, + ); + } + + await this.fieldMetadataService.deleteOne(fieldMetadataToDelete.id); + } + } + + private async createFieldMetadataForForeignTableColumn( + workspaceId: string, + columnName: string, + columnType: string, + objectMetadataId: string, + ): Promise> { + return this.fieldMetadataService.createOne({ + name: columnName, + label: camelToTitleCase(columnName), + description: 'Field of remote', + type: mapUdtNameToFieldType(columnType), + workspaceId: workspaceId, + objectMetadataId: objectMetadataId, + isRemoteCreation: true, + isNullable: true, + icon: 'IconPlug', + settings: mapUdtNameToFieldSettings(columnType), + } satisfies CreateFieldInput); + } }