diff --git a/packages/twenty-front/src/generated-metadata/gql.ts b/packages/twenty-front/src/generated-metadata/gql.ts index 015c5054f..2a0c2ef85 100644 --- a/packages/twenty-front/src/generated-metadata/gql.ts +++ b/packages/twenty-front/src/generated-metadata/gql.ts @@ -14,14 +14,14 @@ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/ */ const documents = { "\n fragment RemoteServerFields on RemoteServer {\n id\n createdAt\n foreignDataWrapperId\n foreignDataWrapperOptions\n foreignDataWrapperType\n userMappingOptions {\n user\n }\n updatedAt\n schema\n }\n": types.RemoteServerFieldsFragmentDoc, - "\n fragment RemoteTableFields on RemoteTable {\n id\n name\n schema\n status\n }\n": types.RemoteTableFieldsFragmentDoc, + "\n fragment RemoteTableFields on RemoteTable {\n id\n name\n schema\n status\n schemaPendingUpdates\n }\n": types.RemoteTableFieldsFragmentDoc, "\n \n mutation createServer($input: CreateRemoteServerInput!) {\n createOneRemoteServer(input: $input) {\n ...RemoteServerFields\n }\n }\n": types.CreateServerDocument, "\n mutation deleteServer($input: RemoteServerIdInput!) {\n deleteOneRemoteServer(input: $input) {\n id\n }\n }\n": types.DeleteServerDocument, "\n \n mutation syncRemoteTable($input: RemoteTableInput!) {\n syncRemoteTable(input: $input) {\n ...RemoteTableFields\n }\n }\n": types.SyncRemoteTableDocument, "\n \n mutation unsyncRemoteTable($input: RemoteTableInput!) {\n unsyncRemoteTable(input: $input) {\n ...RemoteTableFields\n }\n }\n": types.UnsyncRemoteTableDocument, "\n \n mutation updateServer($input: UpdateRemoteServerInput!) {\n updateOneRemoteServer(input: $input) {\n ...RemoteServerFields\n }\n }\n": types.UpdateServerDocument, "\n \n query GetManyDatabaseConnections($input: RemoteServerTypeInput!) {\n findManyRemoteServersByType(input: $input) {\n ...RemoteServerFields\n }\n }\n": types.GetManyDatabaseConnectionsDocument, - "\n \n query GetManyRemoteTables($input: RemoteServerIdInput!) {\n findAvailableRemoteTablesByServerId(input: $input) {\n ...RemoteTableFields\n }\n }\n": types.GetManyRemoteTablesDocument, + "\n \n query GetManyRemoteTables($input: FindManyRemoteTablesInput!) {\n findAvailableRemoteTablesByServerId(input: $input) {\n ...RemoteTableFields\n }\n }\n": types.GetManyRemoteTablesDocument, "\n \n query GetOneDatabaseConnection($input: RemoteServerIdInput!) {\n findOneRemoteServerById(input: $input) {\n ...RemoteServerFields\n }\n }\n": types.GetOneDatabaseConnectionDocument, "\n mutation CreateOneObjectMetadataItem($input: CreateOneObjectInput!) {\n createOneObject(input: $input) {\n id\n dataSourceId\n nameSingular\n namePlural\n labelSingular\n labelPlural\n description\n icon\n isCustom\n isActive\n createdAt\n updatedAt\n labelIdentifierFieldMetadataId\n imageIdentifierFieldMetadataId\n }\n }\n": types.CreateOneObjectMetadataItemDocument, "\n mutation CreateOneFieldMetadataItem($input: CreateOneFieldMetadataInput!) {\n createOneField(input: $input) {\n id\n type\n name\n label\n description\n icon\n isCustom\n isActive\n isNullable\n createdAt\n updatedAt\n defaultValue\n options\n }\n }\n": types.CreateOneFieldMetadataItemDocument, @@ -55,7 +55,7 @@ export function graphql(source: "\n fragment RemoteServerFields on RemoteServer /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "\n fragment RemoteTableFields on RemoteTable {\n id\n name\n schema\n status\n }\n"): (typeof documents)["\n fragment RemoteTableFields on RemoteTable {\n id\n name\n schema\n status\n }\n"]; +export function graphql(source: "\n fragment RemoteTableFields on RemoteTable {\n id\n name\n schema\n status\n schemaPendingUpdates\n }\n"): (typeof documents)["\n fragment RemoteTableFields on RemoteTable {\n id\n name\n schema\n status\n schemaPendingUpdates\n }\n"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -83,7 +83,7 @@ export function graphql(source: "\n \n query GetManyDatabaseConnections($input /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "\n \n query GetManyRemoteTables($input: RemoteServerIdInput!) {\n findAvailableRemoteTablesByServerId(input: $input) {\n ...RemoteTableFields\n }\n }\n"): (typeof documents)["\n \n query GetManyRemoteTables($input: RemoteServerIdInput!) {\n findAvailableRemoteTablesByServerId(input: $input) {\n ...RemoteTableFields\n }\n }\n"]; +export function graphql(source: "\n \n query GetManyRemoteTables($input: FindManyRemoteTablesInput!) {\n findAvailableRemoteTablesByServerId(input: $input) {\n ...RemoteTableFields\n }\n }\n"): (typeof documents)["\n \n query GetManyRemoteTables($input: FindManyRemoteTablesInput!) {\n findAvailableRemoteTablesByServerId(input: $input) {\n ...RemoteTableFields\n }\n }\n"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index c1fe719fd..ef3678f15 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -327,6 +327,13 @@ export enum FileFolder { WorkspaceLogo = 'WorkspaceLogo' } +export type FindManyRemoteTablesInput = { + /** The id of the remote server. */ + id: Scalars['ID']['input']; + /** Indicates if data from distant tables should be refreshed. */ + refreshData?: InputMaybe; +}; + export type FullName = { __typename?: 'FullName'; firstName: Scalars['String']['output']; @@ -668,7 +675,7 @@ export type QueryFieldsArgs = { export type QueryFindAvailableRemoteTablesByServerIdArgs = { - input: RemoteServerIdInput; + input: FindManyRemoteTablesInput; }; @@ -805,6 +812,7 @@ export type RemoteTable = { id?: Maybe; name: Scalars['String']['output']; schema?: Maybe; + schemaPendingUpdates?: Maybe>; status: RemoteTableStatus; }; @@ -849,6 +857,14 @@ export type Support = { supportFrontChatId?: Maybe; }; +/** Schema update on a table */ +export enum TableUpdate { + ColumnsAdded = 'COLUMNS_ADDED', + ColumnsDeleted = 'COLUMNS_DELETED', + ColumnsTypeChanged = 'COLUMNS_TYPE_CHANGED', + TableDeleted = 'TABLE_DELETED' +} + export type Telemetry = { __typename?: 'Telemetry'; anonymizationEnabled: Scalars['Boolean']['output']; @@ -1245,7 +1261,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 RemoteTableFieldsFragment = { __typename?: 'RemoteTable', id?: any | null, name: string, schema?: string | null, status: RemoteTableStatus }; +export type RemoteTableFieldsFragment = { __typename?: 'RemoteTable', id?: any | null, name: string, schema?: string | null, status: RemoteTableStatus, schemaPendingUpdates?: Array | null }; export type CreateServerMutationVariables = Exact<{ input: CreateRemoteServerInput; @@ -1266,14 +1282,14 @@ export type SyncRemoteTableMutationVariables = Exact<{ }>; -export type SyncRemoteTableMutation = { __typename?: 'Mutation', syncRemoteTable: { __typename?: 'RemoteTable', id?: any | null, name: string, schema?: string | null, status: RemoteTableStatus } }; +export type SyncRemoteTableMutation = { __typename?: 'Mutation', syncRemoteTable: { __typename?: 'RemoteTable', id?: any | null, name: string, schema?: string | null, status: RemoteTableStatus, schemaPendingUpdates?: Array | null } }; export type UnsyncRemoteTableMutationVariables = Exact<{ input: RemoteTableInput; }>; -export type UnsyncRemoteTableMutation = { __typename?: 'Mutation', unsyncRemoteTable: { __typename?: 'RemoteTable', id?: any | null, name: string, schema?: string | null, status: RemoteTableStatus } }; +export type UnsyncRemoteTableMutation = { __typename?: 'Mutation', unsyncRemoteTable: { __typename?: 'RemoteTable', id?: any | null, name: string, schema?: string | null, status: RemoteTableStatus, schemaPendingUpdates?: Array | null } }; export type UpdateServerMutationVariables = Exact<{ input: UpdateRemoteServerInput; @@ -1290,11 +1306,11 @@ export type GetManyDatabaseConnectionsQueryVariables = Exact<{ export type GetManyDatabaseConnectionsQuery = { __typename?: 'Query', findManyRemoteServersByType: Array<{ __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 GetManyRemoteTablesQueryVariables = Exact<{ - input: RemoteServerIdInput; + input: FindManyRemoteTablesInput; }>; -export type GetManyRemoteTablesQuery = { __typename?: 'Query', findAvailableRemoteTablesByServerId: Array<{ __typename?: 'RemoteTable', id?: any | null, name: string, schema?: string | null, status: RemoteTableStatus }> }; +export type GetManyRemoteTablesQuery = { __typename?: 'Query', findAvailableRemoteTablesByServerId: Array<{ __typename?: 'RemoteTable', id?: any | null, name: string, schema?: string | null, status: RemoteTableStatus, schemaPendingUpdates?: Array | null }> }; export type GetOneDatabaseConnectionQueryVariables = Exact<{ input: RemoteServerIdInput; @@ -1370,14 +1386,14 @@ export type ObjectMetadataItemsQueryVariables = Exact<{ export type ObjectMetadataItemsQuery = { __typename?: 'Query', objects: { __typename?: 'ObjectConnection', edges: Array<{ __typename?: 'objectEdge', node: { __typename?: 'object', id: any, dataSourceId: string, nameSingular: string, namePlural: string, labelSingular: string, labelPlural: string, description?: string | null, icon?: string | null, isCustom: boolean, isRemote: boolean, isActive: boolean, isSystem: boolean, createdAt: any, updatedAt: any, labelIdentifierFieldMetadataId?: string | null, imageIdentifierFieldMetadataId?: string | null, fields: { __typename?: 'ObjectFieldsConnection', edges: Array<{ __typename?: 'fieldEdge', node: { __typename?: 'field', id: any, type: FieldMetadataType, name: string, label: string, description?: string | null, icon?: string | null, isCustom?: boolean | null, isActive?: boolean | null, isSystem?: boolean | null, isNullable?: boolean | null, createdAt: any, updatedAt: any, defaultValue?: any | null, options?: any | null, fromRelationMetadata?: { __typename?: 'relation', id: any, relationType: RelationMetadataType, toFieldMetadataId: string, toObjectMetadata: { __typename?: 'object', id: any, dataSourceId: string, nameSingular: string, namePlural: string, isSystem: boolean, isRemote: boolean } } | null, toRelationMetadata?: { __typename?: 'relation', id: any, relationType: RelationMetadataType, fromFieldMetadataId: string, fromObjectMetadata: { __typename?: 'object', id: any, dataSourceId: string, nameSingular: string, namePlural: string, isSystem: boolean, isRemote: boolean } } | null, relationDefinition?: { __typename?: 'RelationDefinition', relationId: any, direction: RelationDefinitionType, sourceObjectMetadata: { __typename?: 'object', id: any, nameSingular: string, namePlural: string }, sourceFieldMetadata: { __typename?: 'field', id: any, name: string }, targetObjectMetadata: { __typename?: 'object', id: any, nameSingular: string, namePlural: string }, targetFieldMetadata: { __typename?: 'field', id: any, name: string } } | null } }>, pageInfo: { __typename?: 'PageInfo', hasNextPage?: boolean | null, hasPreviousPage?: boolean | null, startCursor?: any | null, endCursor?: any | null } } } }>, pageInfo: { __typename?: 'PageInfo', hasNextPage?: boolean | null, hasPreviousPage?: boolean | null, startCursor?: any | null, endCursor?: any | null } } }; export const RemoteServerFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteServerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteServer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperId"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperOptions"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperType"}},{"kind":"Field","name":{"kind":"Name","value":"userMappingOptions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"}}]}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"schema"}}]}}]} as unknown as DocumentNode; -export const RemoteTableFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteTableFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteTable"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"schema"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}}]} as unknown as DocumentNode; +export const RemoteTableFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteTableFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteTable"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"schema"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"schemaPendingUpdates"}}]}}]} as unknown as DocumentNode; export const CreateServerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"createServer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateRemoteServerInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createOneRemoteServer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"RemoteServerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteServerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteServer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperId"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperOptions"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperType"}},{"kind":"Field","name":{"kind":"Name","value":"userMappingOptions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"}}]}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"schema"}}]}}]} as unknown as DocumentNode; export const DeleteServerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"deleteServer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteServerIdInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deleteOneRemoteServer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode; -export const SyncRemoteTableDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"syncRemoteTable"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteTableInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"syncRemoteTable"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"RemoteTableFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteTableFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteTable"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"schema"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}}]} as unknown as DocumentNode; -export const UnsyncRemoteTableDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"unsyncRemoteTable"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteTableInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"unsyncRemoteTable"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"RemoteTableFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteTableFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteTable"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"schema"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}}]} as unknown as DocumentNode; +export const SyncRemoteTableDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"syncRemoteTable"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteTableInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"syncRemoteTable"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"RemoteTableFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteTableFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteTable"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"schema"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"schemaPendingUpdates"}}]}}]} as unknown as DocumentNode; +export const UnsyncRemoteTableDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"unsyncRemoteTable"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteTableInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"unsyncRemoteTable"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"RemoteTableFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteTableFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteTable"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"schema"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"schemaPendingUpdates"}}]}}]} as unknown as DocumentNode; export const UpdateServerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"updateServer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateRemoteServerInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateOneRemoteServer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"RemoteServerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteServerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteServer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperId"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperOptions"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperType"}},{"kind":"Field","name":{"kind":"Name","value":"userMappingOptions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"}}]}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"schema"}}]}}]} as unknown as DocumentNode; export const GetManyDatabaseConnectionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetManyDatabaseConnections"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteServerTypeInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findManyRemoteServersByType"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"RemoteServerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteServerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteServer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperId"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperOptions"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperType"}},{"kind":"Field","name":{"kind":"Name","value":"userMappingOptions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"}}]}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"schema"}}]}}]} as unknown as DocumentNode; -export const GetManyRemoteTablesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetManyRemoteTables"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteServerIdInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findAvailableRemoteTablesByServerId"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"RemoteTableFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteTableFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteTable"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"schema"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}}]} as unknown as DocumentNode; +export const GetManyRemoteTablesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetManyRemoteTables"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"FindManyRemoteTablesInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findAvailableRemoteTablesByServerId"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"RemoteTableFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteTableFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteTable"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"schema"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"schemaPendingUpdates"}}]}}]} as unknown as DocumentNode; export const GetOneDatabaseConnectionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOneDatabaseConnection"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteServerIdInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"findOneRemoteServerById"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"RemoteServerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RemoteServerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"RemoteServer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperId"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperOptions"}},{"kind":"Field","name":{"kind":"Name","value":"foreignDataWrapperType"}},{"kind":"Field","name":{"kind":"Name","value":"userMappingOptions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"}}]}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"schema"}}]}}]} as unknown as DocumentNode; export const CreateOneObjectMetadataItemDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateOneObjectMetadataItem"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateOneObjectInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createOneObject"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"dataSourceId"}},{"kind":"Field","name":{"kind":"Name","value":"nameSingular"}},{"kind":"Field","name":{"kind":"Name","value":"namePlural"}},{"kind":"Field","name":{"kind":"Name","value":"labelSingular"}},{"kind":"Field","name":{"kind":"Name","value":"labelPlural"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"labelIdentifierFieldMetadataId"}},{"kind":"Field","name":{"kind":"Name","value":"imageIdentifierFieldMetadataId"}}]}}]}}]} as unknown as DocumentNode; export const CreateOneFieldMetadataItemDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateOneFieldMetadataItem"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateOneFieldMetadataInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createOneField"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"icon"}},{"kind":"Field","name":{"kind":"Name","value":"isCustom"}},{"kind":"Field","name":{"kind":"Name","value":"isActive"}},{"kind":"Field","name":{"kind":"Name","value":"isNullable"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"defaultValue"}},{"kind":"Field","name":{"kind":"Name","value":"options"}}]}}]}}]} as unknown as DocumentNode; diff --git a/packages/twenty-front/src/modules/databases/graphql/fragments/remoteTableFragment.ts b/packages/twenty-front/src/modules/databases/graphql/fragments/remoteTableFragment.ts index 9d6e0488b..2841b60c0 100644 --- a/packages/twenty-front/src/modules/databases/graphql/fragments/remoteTableFragment.ts +++ b/packages/twenty-front/src/modules/databases/graphql/fragments/remoteTableFragment.ts @@ -6,5 +6,6 @@ export const REMOTE_TABLE_FRAGMENT = gql` name schema status + schemaPendingUpdates } `; diff --git a/packages/twenty-front/src/modules/databases/graphql/queries/findManyRemoteTables.ts b/packages/twenty-front/src/modules/databases/graphql/queries/findManyRemoteTables.ts index 69eca2bf8..3b57fc435 100644 --- a/packages/twenty-front/src/modules/databases/graphql/queries/findManyRemoteTables.ts +++ b/packages/twenty-front/src/modules/databases/graphql/queries/findManyRemoteTables.ts @@ -4,7 +4,7 @@ import { REMOTE_TABLE_FRAGMENT } from '@/databases/graphql/fragments/remoteTable export const GET_MANY_REMOTE_TABLES = gql` ${REMOTE_TABLE_FRAGMENT} - query GetManyRemoteTables($input: RemoteServerIdInput!) { + query GetManyRemoteTables($input: FindManyRemoteTablesInput!) { findAvailableRemoteTablesByServerId(input: $input) { ...RemoteTableFields } diff --git a/packages/twenty-front/src/modules/databases/hooks/useGetDatabaseConnectionTables.ts b/packages/twenty-front/src/modules/databases/hooks/useGetDatabaseConnectionTables.ts index dd50582bb..064f28c72 100644 --- a/packages/twenty-front/src/modules/databases/hooks/useGetDatabaseConnectionTables.ts +++ b/packages/twenty-front/src/modules/databases/hooks/useGetDatabaseConnectionTables.ts @@ -10,11 +10,13 @@ import { type UseGetDatabaseConnectionTablesParams = { connectionId: string; skip?: boolean; + refreshData?: boolean; }; export const useGetDatabaseConnectionTables = ({ connectionId, skip, + refreshData, }: UseGetDatabaseConnectionTablesParams) => { const apolloMetadataClient = useApolloMetadataClient(); @@ -27,6 +29,7 @@ export const useGetDatabaseConnectionTables = ({ variables: { input: { id: connectionId, + refreshData: refreshData, }, }, }); diff --git a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseTablesListCard.tsx b/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseTablesListCard.tsx index 57db9cd39..465d36c3a 100644 --- a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseTablesListCard.tsx +++ b/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseTablesListCard.tsx @@ -6,7 +6,11 @@ import { useSyncRemoteTable } from '@/databases/hooks/useSyncRemoteTable'; import { useUnsyncRemoteTable } from '@/databases/hooks/useUnsyncRemoteTable'; import { SettingsListCard } from '@/settings/components/SettingsListCard'; import { SettingsIntegrationRemoteTableSyncStatusToggle } from '@/settings/integrations/components/SettingsIntegrationRemoteTableSyncStatusToggle'; -import { RemoteTable, RemoteTableStatus } from '~/generated-metadata/graphql'; +import { + RemoteTable, + RemoteTableStatus, + TableUpdate, +} from '~/generated-metadata/graphql'; import { isDefined } from '~/utils/isDefined'; export const settingsIntegrationsDatabaseTablesSchema = z.object({ @@ -28,6 +32,32 @@ const StyledRowRightContainer = styled.div` gap: ${({ theme }) => theme.spacing(1)}; `; +const StyledText = styled.h3` + color: ${({ theme }) => theme.font.color.tertiary}; + font-size: ${({ theme }) => theme.font.size.md}; + font-weight: ${({ theme }) => theme.font.weight.regular}; + margin: 0; +`; + +const getTableUpdatesText = (schemaPendingUpdates: TableUpdate[]) => { + if (schemaPendingUpdates.includes(TableUpdate.TableDeleted)) { + return 'Table has been deleted'; + } + if ( + schemaPendingUpdates.includes(TableUpdate.ColumnsAdded) && + schemaPendingUpdates.includes(TableUpdate.ColumnsDeleted) + ) { + return 'Columns have been added and other deleted'; + } + if (schemaPendingUpdates.includes(TableUpdate.ColumnsAdded)) { + return 'Columns have been added'; + } + if (schemaPendingUpdates.includes(TableUpdate.ColumnsDeleted)) { + return 'Columns have been deleted'; + } + return null; +}; + export const SettingsIntegrationDatabaseTablesListCard = ({ connectionId, tables, @@ -40,6 +70,9 @@ export const SettingsIntegrationDatabaseTablesListCard = ({ tables.map((table) => ({ ...table, id: table.name, + updatesText: table.schemaPendingUpdates + ? getTableUpdatesText(table.schemaPendingUpdates) + : null, })), ); @@ -68,9 +101,15 @@ export const SettingsIntegrationDatabaseTablesListCard = ({ ({ item, }: { - item: { id: string; name: string; status: RemoteTableStatus }; + item: { + id: string; + name: string; + status: RemoteTableStatus; + updatesText?: string | null; + }; }) => ( + {item.updatesText && {item.updatesText}} { const { tables } = useGetDatabaseConnectionTables({ connectionId, skip: !connection, + refreshData: true, }); return { connection, integration, databaseKey, tables }; diff --git a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-server.service.ts b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-server.service.ts index 8c56e393d..b7ad3a2e7 100644 --- a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-server.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-server.service.ts @@ -126,7 +126,7 @@ export class RemoteServerService { } const currentRemoteTablesForServer = - await this.remoteTableService.findCurrentRemoteTablesByServerId({ + await this.remoteTableService.findRemoteTablesByServerId({ remoteServerId: remoteServer.id, workspaceId, }); 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 9905da7a5..4d6487307 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 @@ -23,10 +23,10 @@ export class DistantTableService { >, ) {} - public async fetchDistantTableColumns( + public getDistantTableColumns( remoteServer: RemoteServerEntity, tableName: string, - ): Promise { + ): DistantTableColumn[] { if (!remoteServer.availableTables) { throw new BadRequestException( 'Remote server available tables are not defined', @@ -36,15 +36,16 @@ export class DistantTableService { return remoteServer.availableTables[tableName]; } - public async fetchDistantTableNames( + public async fetchDistantTables( remoteServer: RemoteServerEntity, workspaceId: string, - ): Promise { - const availableTables = - remoteServer.availableTables ?? - (await this.createAvailableTables(remoteServer, workspaceId)); + refreshData?: boolean, + ): Promise { + if (!refreshData && remoteServer.availableTables) { + return remoteServer.availableTables; + } - return Object.keys(availableTables); + return await this.createAvailableTables(remoteServer, workspaceId); } private async createAvailableTables( diff --git a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/dtos/find-many-remote-tables-input.ts b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/dtos/find-many-remote-tables-input.ts new file mode 100644 index 000000000..515962d40 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/dtos/find-many-remote-tables-input.ts @@ -0,0 +1,17 @@ +import { InputType, ID, Field } from '@nestjs/graphql'; + +import { IDField } from '@ptc-org/nestjs-query-graphql'; +import { IsOptional } from 'class-validator'; + +@InputType() +export class FindManyRemoteTablesInput { + @IDField(() => ID, { description: 'The id of the remote server.' }) + id!: string; + + @IsOptional() + @Field(() => Boolean, { + description: 'Indicates if data from distant tables should be refreshed.', + nullable: true, + }) + refreshData?: boolean; +} diff --git a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/dtos/remote-table.dto.ts b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/dtos/remote-table.dto.ts index 07dd57170..d22ee19ee 100644 --- a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/dtos/remote-table.dto.ts +++ b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/dtos/remote-table.dto.ts @@ -1,7 +1,7 @@ import { ObjectType, Field, registerEnumType } from '@nestjs/graphql'; import { IDField } from '@ptc-org/nestjs-query-graphql'; -import { IsEnum, IsOptional } from 'class-validator'; +import { IsOptional } from 'class-validator'; import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; @@ -10,11 +10,23 @@ export enum RemoteTableStatus { NOT_SYNCED = 'NOT_SYNCED', } +export enum TableUpdate { + TABLE_DELETED = 'TABLE_DELETED', + COLUMNS_DELETED = 'COLUMN_DELETED', + COLUMNS_ADDED = 'COLUMN_ADDED', + COLUMNS_TYPE_CHANGED = 'COLUMN_TYPE_CHANGED', +} + registerEnumType(RemoteTableStatus, { name: 'RemoteTableStatus', description: 'Status of the table', }); +registerEnumType(TableUpdate, { + name: 'TableUpdate', + description: 'Schema update on a table', +}); + @ObjectType('RemoteTable') export class RemoteTableDTO { @IDField(() => UUIDScalarType, { nullable: true }) @@ -23,11 +35,14 @@ export class RemoteTableDTO { @Field(() => String) name: string; - @IsEnum(RemoteTableStatus) @Field(() => RemoteTableStatus) status: RemoteTableStatus; @IsOptional() @Field(() => String, { nullable: true }) schema?: string; + + @IsOptional() + @Field(() => [TableUpdate], { nullable: true }) + schemaPendingUpdates?: [TableUpdate]; } diff --git a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/remote-table.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/remote-table.resolver.ts index 5278c58c9..aa103d09b 100644 --- a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/remote-table.resolver.ts +++ b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/remote-table.resolver.ts @@ -4,7 +4,7 @@ import { Args, Mutation, Query, Resolver } from '@nestjs/graphql'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator'; import { JwtAuthGuard } from 'src/engine/guards/jwt.auth.guard'; -import { RemoteServerIdInput } from 'src/engine/metadata-modules/remote-server/dtos/remote-server-id.input'; +import { FindManyRemoteTablesInput } from 'src/engine/metadata-modules/remote-server/remote-table/dtos/find-many-remote-tables-input'; import { RemoteTableInput } from 'src/engine/metadata-modules/remote-server/remote-table/dtos/remote-table-input'; import { RemoteTableDTO } from 'src/engine/metadata-modules/remote-server/remote-table/dtos/remote-table.dto'; import { RemoteTableService } from 'src/engine/metadata-modules/remote-server/remote-table/remote-table.service'; @@ -16,12 +16,13 @@ export class RemoteTableResolver { @Query(() => [RemoteTableDTO]) async findAvailableRemoteTablesByServerId( - @Args('input') { id }: RemoteServerIdInput, + @Args('input') input: FindManyRemoteTablesInput, @AuthWorkspace() { id: workspaceId }: Workspace, ) { - return this.remoteTableService.findAvailableRemoteTablesByServerId( - id, + return this.remoteTableService.findDistantTablesByServerId( + input.id, workspaceId, + input.refreshData, ); } 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 ae2d34bcf..9d3e1b6bd 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 @@ -8,7 +8,10 @@ import { RemoteServerType, RemoteServerEntity, } from 'src/engine/metadata-modules/remote-server/remote-server.entity'; -import { RemoteTableStatus } from 'src/engine/metadata-modules/remote-server/remote-table/dtos/remote-table.dto'; +import { + RemoteTableStatus, + TableUpdate, +} from 'src/engine/metadata-modules/remote-server/remote-table/dtos/remote-table.dto'; import { mapUdtNameToFieldType, mapUdtNameToFieldSettings, @@ -36,6 +39,8 @@ import { RemoteTableEntity } from 'src/engine/metadata-modules/remote-server/rem 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 { 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 { RemoteTableSchemaColumn } from 'src/engine/metadata-modules/remote-server/remote-table/types/remote-table-schema-column'; export class RemoteTableService { private readonly logger = new Logger(RemoteTableService.name); @@ -57,9 +62,10 @@ export class RemoteTableService { private readonly workspaceDataSourceService: WorkspaceDataSourceService, ) {} - public async findAvailableRemoteTablesByServerId( + public async findDistantTablesByServerId( id: string, workspaceId: string, + refreshData?: boolean, ) { const remoteServer = await this.remoteServerRepository.findOne({ where: { @@ -72,7 +78,7 @@ export class RemoteTableService { throw new NotFoundException('Remote server does not exist'); } - const currentRemoteTables = await this.findCurrentRemoteTablesByServerId({ + const currentRemoteTables = await this.findRemoteTablesByServerId({ remoteServerId: id, workspaceId, }); @@ -81,22 +87,113 @@ export class RemoteTableService { (remoteTable) => remoteTable.distantTableName, ); - const distantTableNames = - await this.distantTableService.fetchDistantTableNames( - remoteServer, - workspaceId, + const distantTables = await this.distantTableService.fetchDistantTables( + remoteServer, + workspaceId, + refreshData, + ); + + if (!refreshData || currentRemoteTables.length === 0) { + const distantTablesWithStatus = Object.keys(distantTables).map( + (tableName) => ({ + name: tableName, + schema: remoteServer.schema, + status: currentRemoteTableDistantNames.includes(tableName) + ? RemoteTableStatus.SYNCED + : RemoteTableStatus.NOT_SYNCED, + }), ); - return distantTableNames.map((tableName) => ({ - name: tableName, - schema: remoteServer.schema, - status: currentRemoteTableDistantNames.includes(tableName) - ? RemoteTableStatus.SYNCED - : RemoteTableStatus.NOT_SYNCED, - })); + return distantTablesWithStatus; + } + + const schemaPendingUpdates = + await this.getSchemaUpdatesBetweenRemoteAndDistantTables({ + workspaceId, + remoteTables: currentRemoteTables, + distantTables, + }); + + const distantTablesWithUpdates = Object.keys(distantTables).map( + (tableName) => ({ + name: tableName, + schema: remoteServer.schema, + status: currentRemoteTableDistantNames.includes(tableName) + ? RemoteTableStatus.SYNCED + : RemoteTableStatus.NOT_SYNCED, + schemaPendingUpdates: schemaPendingUpdates[tableName], + }), + ); + + const deletedTables = Object.entries(schemaPendingUpdates) + .filter(([_tableName, updates]) => + updates.includes(TableUpdate.TABLE_DELETED), + ) + .map(([tableName, updates]) => ({ + name: tableName, + schema: remoteServer.schema, + status: RemoteTableStatus.SYNCED, + schemaPendingUpdates: updates, + })); + + return distantTablesWithUpdates.concat(deletedTables); } - public async findCurrentRemoteTablesByServerId({ + private async getSchemaUpdatesBetweenRemoteAndDistantTables({ + workspaceId, + remoteTables, + distantTables, + }: { + workspaceId: string; + remoteTables: RemoteTableEntity[]; + distantTables: DistantTables; + }): Promise<{ [tablename: string]: TableUpdate[] }> { + const updates = {}; + + for (const remoteTable of remoteTables) { + const distantTable = distantTables[remoteTable.distantTableName]; + const tableName = remoteTable.distantTableName; + + if (!distantTable) { + updates[tableName] = [TableUpdate.TABLE_DELETED]; + continue; + } + + const distantColumnNames = new Set( + distantTable.map((column) => column.columnName), + ); + const localColumnNames = new Set( + ( + await this.fetchTableColumns(workspaceId, remoteTable.localTableName) + ).map((column) => column.column_name), + ); + + const columnsAdded = [...distantColumnNames].filter( + (columnName) => !localColumnNames.has(columnName), + ); + + const columnsDeleted = [...localColumnNames].filter( + (columnName) => !distantColumnNames.has(columnName), + ); + + if (columnsAdded.length > 0) { + updates[tableName] = [ + ...(updates[tableName] || []), + TableUpdate.COLUMNS_ADDED, + ]; + } + if (columnsDeleted.length > 0) { + updates[tableName] = [ + ...(updates[tableName] || []), + TableUpdate.COLUMNS_DELETED, + ]; + } + } + + return updates; + } + + public async findRemoteTablesByServerId({ remoteServerId, workspaceId, }: { @@ -156,11 +253,10 @@ export class RemoteTableService { remoteServerId: remoteServer.id, }); - const distantTableColumns = - await this.distantTableService.fetchDistantTableColumns( - remoteServer, - input.name, - ); + const distantTableColumns = this.distantTableService.getDistantTableColumns( + remoteServer, + input.name, + ); // We only support remote tables with an id column for now. const distantTableIdColumn = distantTableColumns.find( @@ -332,6 +428,25 @@ export class RemoteTableService { ).map((foreignTable) => foreignTable.foreign_table_name); } + private async fetchTableColumns( + workspaceId: string, + tableName: string, + ): Promise { + const workspaceDataSource = + await this.workspaceDataSourceService.connectToWorkspaceDataSource( + workspaceId, + ); + + const schemaName = + this.workspaceDataSourceService.getSchemaName(workspaceId); + + return await workspaceDataSource.query( + `SELECT column_name, data_type + FROM information_schema.columns + WHERE table_schema = '${schemaName}' AND table_name = '${tableName}'`, + ); + } + private async createForeignTable( workspaceId: string, localTableName: string, diff --git a/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/types/remote-table-schema-column.ts b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/types/remote-table-schema-column.ts new file mode 100644 index 000000000..c91b7ac27 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/remote-server/remote-table/types/remote-table-schema-column.ts @@ -0,0 +1,4 @@ +export type RemoteTableSchemaColumn = { + column_name: string; + data_type: string; +};