Add connection failed status (#4939)
1/ When the user inputs wrong connection informations, we do not inform him. He will only see that no tables are available. We will display a connection failed status if an error is raised testing the connection 2/ If the connection fails, it should still be possible to delete the server. Today, since we try first to delete the tables, the connection failure throws an error that will prevent server deletion. Using the foreign tables instead of calling the distant DB. 3/ Redirect to connection show page instead of connection list after creation 4/ Today, foreign tables are fetched without the server name. This is a mistake because we need to know which foreign table is linked with which server. Updating the associated query. <img width="632" alt="Capture d’écran 2024-04-12 à 10 52 49" src="https://github.com/twentyhq/twenty/assets/22936103/9e8406b8-75d0-494c-ac1f-5e9fa7100f5c"> --------- Co-authored-by: Thomas Trompette <thomast@twenty.com>
This commit is contained in:
@ -18,7 +18,7 @@ export const useGetDatabaseConnectionTables = ({
|
|||||||
}: UseGetDatabaseConnectionTablesParams) => {
|
}: UseGetDatabaseConnectionTablesParams) => {
|
||||||
const apolloMetadataClient = useApolloMetadataClient();
|
const apolloMetadataClient = useApolloMetadataClient();
|
||||||
|
|
||||||
const { data } = useQuery<
|
const { data, error } = useQuery<
|
||||||
GetManyRemoteTablesQuery,
|
GetManyRemoteTablesQuery,
|
||||||
GetManyRemoteTablesQueryVariables
|
GetManyRemoteTablesQueryVariables
|
||||||
>(GET_MANY_REMOTE_TABLES, {
|
>(GET_MANY_REMOTE_TABLES, {
|
||||||
@ -33,5 +33,6 @@ export const useGetDatabaseConnectionTables = ({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
tables: data?.findAvailableRemoteTablesByServerId || [],
|
tables: data?.findAvailableRemoteTablesByServerId || [],
|
||||||
|
error,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
|
import { useContext } from 'react';
|
||||||
import { EntityChip } from 'twenty-ui';
|
import { EntityChip } from 'twenty-ui';
|
||||||
|
|
||||||
import { useMapToObjectRecordIdentifier } from '@/object-metadata/hooks/useMapToObjectRecordIdentifier';
|
import { useMapToObjectRecordIdentifier } from '@/object-metadata/hooks/useMapToObjectRecordIdentifier';
|
||||||
|
import { RecordTableRowContext } from '@/object-record/record-table/contexts/RecordTableRowContext';
|
||||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||||
import { getImageAbsoluteURIOrBase64 } from '~/utils/image/getImageAbsoluteURIOrBase64';
|
import { getImageAbsoluteURIOrBase64 } from '~/utils/image/getImageAbsoluteURIOrBase64';
|
||||||
|
|
||||||
@ -21,8 +23,16 @@ export const RecordChip = ({
|
|||||||
objectNameSingular,
|
objectNameSingular,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Will only exists if the chip is inside a record table.
|
||||||
|
// This is temporary until we have the show page for remote objects.
|
||||||
|
const { isReadOnly } = useContext(RecordTableRowContext);
|
||||||
|
|
||||||
const objectRecordIdentifier = mapToObjectRecordIdentifier(record);
|
const objectRecordIdentifier = mapToObjectRecordIdentifier(record);
|
||||||
|
|
||||||
|
const linkToEntity = isReadOnly
|
||||||
|
? undefined
|
||||||
|
: objectRecordIdentifier.linkToShowPage;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EntityChip
|
<EntityChip
|
||||||
entityId={record.id}
|
entityId={record.id}
|
||||||
@ -31,7 +41,7 @@ export const RecordChip = ({
|
|||||||
avatarUrl={
|
avatarUrl={
|
||||||
getImageAbsoluteURIOrBase64(objectRecordIdentifier.avatarUrl) || ''
|
getImageAbsoluteURIOrBase64(objectRecordIdentifier.avatarUrl) || ''
|
||||||
}
|
}
|
||||||
linkToEntity={objectRecordIdentifier.linkToShowPage}
|
linkToEntity={linkToEntity}
|
||||||
maxWidth={maxWidth}
|
maxWidth={maxWidth}
|
||||||
className={className}
|
className={className}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { useGetDatabaseConnectionTables } from '@/databases/hooks/useGetDatabaseConnectionTables';
|
import { useGetDatabaseConnectionTables } from '@/databases/hooks/useGetDatabaseConnectionTables';
|
||||||
import { Status } from '@/ui/display/status/components/Status';
|
import { Status } from '@/ui/display/status/components/Status';
|
||||||
import { RemoteTableStatus } from '~/generated-metadata/graphql';
|
import { RemoteTableStatus } from '~/generated-metadata/graphql';
|
||||||
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
type SettingsIntegrationDatabaseConnectionSyncStatusProps = {
|
type SettingsIntegrationDatabaseConnectionSyncStatusProps = {
|
||||||
connectionId: string;
|
connectionId: string;
|
||||||
@ -11,11 +12,15 @@ export const SettingsIntegrationDatabaseConnectionSyncStatus = ({
|
|||||||
connectionId,
|
connectionId,
|
||||||
skip,
|
skip,
|
||||||
}: SettingsIntegrationDatabaseConnectionSyncStatusProps) => {
|
}: SettingsIntegrationDatabaseConnectionSyncStatusProps) => {
|
||||||
const { tables } = useGetDatabaseConnectionTables({
|
const { tables, error } = useGetDatabaseConnectionTables({
|
||||||
connectionId,
|
connectionId,
|
||||||
skip,
|
skip,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (isDefined(error)) {
|
||||||
|
return <Status color="red" text="Connection failed" />;
|
||||||
|
}
|
||||||
|
|
||||||
const syncedTables = tables.filter(
|
const syncedTables = tables.filter(
|
||||||
(table) => table.status === RemoteTableStatus.Synced,
|
(table) => table.status === RemoteTableStatus.Synced,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -35,8 +35,11 @@ export const RecordIndexPageHeader = ({
|
|||||||
const canAddRecord =
|
const canAddRecord =
|
||||||
recordIndexViewType === ViewType.Table && !objectMetadataItem?.isRemote;
|
recordIndexViewType === ViewType.Table && !objectMetadataItem?.isRemote;
|
||||||
|
|
||||||
|
const pageHeaderTitle =
|
||||||
|
objectMetadataItem?.labelPlural ?? capitalize(objectNamePlural);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageHeader title={capitalize(objectNamePlural)} Icon={Icon}>
|
<PageHeader title={pageHeaderTitle} Icon={Icon}>
|
||||||
<PageHotkeysEffect onAddButtonClick={createRecord} />
|
<PageHotkeysEffect onAddButtonClick={createRecord} />
|
||||||
{canAddRecord && <PageAddButton onClick={createRecord} />}
|
{canAddRecord && <PageAddButton onClick={createRecord} />}
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|||||||
@ -95,14 +95,18 @@ export const SettingsIntegrationNewDatabaseConnection = () => {
|
|||||||
const formValues = formConfig.getValues();
|
const formValues = formConfig.getValues();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await createOneDatabaseConnection(
|
const createdConnection = await createOneDatabaseConnection(
|
||||||
createRemoteServerInputSchema.parse({
|
createRemoteServerInputSchema.parse({
|
||||||
...formValues,
|
...formValues,
|
||||||
foreignDataWrapperType: getForeignDataWrapperType(databaseKey),
|
foreignDataWrapperType: getForeignDataWrapperType(databaseKey),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
navigate(`${settingsIntegrationsPagePath}/${databaseKey}`);
|
const connectionId = createdConnection.data?.createOneRemoteServer.id;
|
||||||
|
|
||||||
|
navigate(
|
||||||
|
`${settingsIntegrationsPagePath}/${databaseKey}/${connectionId}`,
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
enqueueSnackBar((error as Error).message, {
|
enqueueSnackBar((error as Error).message, {
|
||||||
variant: 'error',
|
variant: 'error',
|
||||||
|
|||||||
@ -361,7 +361,7 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
|||||||
[
|
[
|
||||||
createdObjectMetadata.id,
|
createdObjectMetadata.id,
|
||||||
'table',
|
'table',
|
||||||
`All ${createdObjectMetadata.namePlural}`,
|
`All ${createdObjectMetadata.labelPlural}`,
|
||||||
'INDEX',
|
'INDEX',
|
||||||
createdObjectMetadata.icon,
|
createdObjectMetadata.icon,
|
||||||
],
|
],
|
||||||
|
|||||||
@ -17,7 +17,6 @@ import {
|
|||||||
} from 'src/engine/metadata-modules/remote-server/utils/validate-remote-server-input';
|
} from 'src/engine/metadata-modules/remote-server/utils/validate-remote-server-input';
|
||||||
import { ForeignDataWrapperQueryFactory } from 'src/engine/api/graphql/workspace-query-builder/factories/foreign-data-wrapper-query.factory';
|
import { ForeignDataWrapperQueryFactory } from 'src/engine/api/graphql/workspace-query-builder/factories/foreign-data-wrapper-query.factory';
|
||||||
import { RemoteTableService } from 'src/engine/metadata-modules/remote-server/remote-table/remote-table.service';
|
import { RemoteTableService } from 'src/engine/metadata-modules/remote-server/remote-table/remote-table.service';
|
||||||
import { RemoteTableStatus } from 'src/engine/metadata-modules/remote-server/remote-table/dtos/remote-table.dto';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RemoteServerService<T extends RemoteServerType> {
|
export class RemoteServerService<T extends RemoteServerType> {
|
||||||
@ -117,21 +116,18 @@ export class RemoteServerService<T extends RemoteServerType> {
|
|||||||
throw new NotFoundException('Object does not exist');
|
throw new NotFoundException('Object does not exist');
|
||||||
}
|
}
|
||||||
|
|
||||||
const remoteTablesToRemove = (
|
const foreignTablesToRemove =
|
||||||
await this.remoteTableService.findAvailableRemoteTablesByServerId(
|
await this.remoteTableService.fetchForeignTableNamesWithinWorkspace(
|
||||||
id,
|
|
||||||
workspaceId,
|
workspaceId,
|
||||||
)
|
remoteServer.foreignDataWrapperId,
|
||||||
).filter((remoteTable) => remoteTable.status === RemoteTableStatus.SYNCED);
|
);
|
||||||
|
|
||||||
if (remoteTablesToRemove.length) {
|
if (foreignTablesToRemove.length) {
|
||||||
for (const remoteTable of remoteTablesToRemove) {
|
for (const foreignTableName of foreignTablesToRemove) {
|
||||||
await this.remoteTableService.unsyncRemoteTable(
|
await this.remoteTableService.removeForeignTableAndMetadata(
|
||||||
{
|
foreignTableName,
|
||||||
remoteServerId: id,
|
|
||||||
name: remoteTable.name,
|
|
||||||
},
|
|
||||||
workspaceId,
|
workspaceId,
|
||||||
|
remoteServer,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,7 +26,7 @@ import { RemotePostgresTableService } from 'src/engine/metadata-modules/remote-s
|
|||||||
import { WorkspaceCacheVersionService } from 'src/engine/metadata-modules/workspace-cache-version/workspace-cache-version.service';
|
import { WorkspaceCacheVersionService } from 'src/engine/metadata-modules/workspace-cache-version/workspace-cache-version.service';
|
||||||
import { camelCase } from 'src/utils/camel-case';
|
import { camelCase } from 'src/utils/camel-case';
|
||||||
import { camelToTitleCase } from 'src/utils/camel-to-title-case';
|
import { camelToTitleCase } from 'src/utils/camel-to-title-case';
|
||||||
import { getRemoteTableName } from 'src/engine/metadata-modules/remote-server/remote-table/utils/get-remote-table-name.util';
|
import { getRemoteTableLocalName } from 'src/engine/metadata-modules/remote-server/remote-table/utils/get-remote-table-local-name.util';
|
||||||
import { WorkspaceMigrationService } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.service';
|
import { WorkspaceMigrationService } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.service';
|
||||||
import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service';
|
import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service';
|
||||||
import { generateMigrationName } from 'src/engine/metadata-modules/workspace-migration/utils/generate-migration-name.util';
|
import { generateMigrationName } from 'src/engine/metadata-modules/workspace-migration/utils/generate-migration-name.util';
|
||||||
@ -73,7 +73,10 @@ export class RemoteTableService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const currentForeignTableNames =
|
const currentForeignTableNames =
|
||||||
await this.fetchForeignTableNamesWithinWorkspace(workspaceId);
|
await this.fetchForeignTableNamesWithinWorkspace(
|
||||||
|
workspaceId,
|
||||||
|
remoteServer.foreignDataWrapperId,
|
||||||
|
);
|
||||||
|
|
||||||
const tableInRemoteSchema =
|
const tableInRemoteSchema =
|
||||||
await this.fetchTablesFromRemoteSchema(remoteServer);
|
await this.fetchTablesFromRemoteSchema(remoteServer);
|
||||||
@ -82,7 +85,7 @@ export class RemoteTableService {
|
|||||||
name: remoteTable.tableName,
|
name: remoteTable.tableName,
|
||||||
schema: remoteTable.tableSchema,
|
schema: remoteTable.tableSchema,
|
||||||
status: currentForeignTableNames.includes(
|
status: currentForeignTableNames.includes(
|
||||||
getRemoteTableName(remoteTable.tableName),
|
getRemoteTableLocalName(remoteTable.tableName),
|
||||||
)
|
)
|
||||||
? RemoteTableStatus.SYNCED
|
? RemoteTableStatus.SYNCED
|
||||||
: RemoteTableStatus.NOT_SYNCED,
|
: RemoteTableStatus.NOT_SYNCED,
|
||||||
@ -107,20 +110,95 @@ export class RemoteTableService {
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
);
|
);
|
||||||
|
|
||||||
await this.workspaceCacheVersionService.incrementVersion(workspaceId);
|
|
||||||
|
|
||||||
return remoteTable;
|
return remoteTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async unsyncRemoteTable(input: RemoteTableInput, workspaceId: string) {
|
public async unsyncRemoteTable(input: RemoteTableInput, workspaceId: string) {
|
||||||
const remoteTable = await this.removeForeignTableAndMetadata(
|
const remoteServer = await this.remoteServerRepository.findOne({
|
||||||
input,
|
where: {
|
||||||
|
id: input.remoteServerId,
|
||||||
|
workspaceId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!remoteServer) {
|
||||||
|
throw new NotFoundException('Remote server does not exist');
|
||||||
|
}
|
||||||
|
|
||||||
|
const remoteTableLocalName = getRemoteTableLocalName(input.name);
|
||||||
|
|
||||||
|
await this.removeForeignTableAndMetadata(
|
||||||
|
remoteTableLocalName,
|
||||||
|
workspaceId,
|
||||||
|
remoteServer,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: input.name,
|
||||||
|
schema: input.schema,
|
||||||
|
status: RemoteTableStatus.NOT_SYNCED,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async fetchForeignTableNamesWithinWorkspace(
|
||||||
|
workspaceId: string,
|
||||||
|
foreignDataWrapperId: string,
|
||||||
|
): Promise<string[]> {
|
||||||
|
const workspaceDataSource =
|
||||||
|
await this.workspaceDataSourceService.connectToWorkspaceDataSource(
|
||||||
|
workspaceId,
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
await workspaceDataSource.query(
|
||||||
|
`SELECT foreign_table_name, foreign_server_name FROM information_schema.foreign_tables WHERE foreign_server_name = '${foreignDataWrapperId}'`,
|
||||||
|
)
|
||||||
|
).map((foreignTable) => foreignTable.foreign_table_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async removeForeignTableAndMetadata(
|
||||||
|
remoteTableLocalName: string,
|
||||||
|
workspaceId: string,
|
||||||
|
remoteServer: RemoteServerEntity<RemoteServerType>,
|
||||||
|
) {
|
||||||
|
const currentForeignTableNames =
|
||||||
|
await this.fetchForeignTableNamesWithinWorkspace(
|
||||||
|
workspaceId,
|
||||||
|
remoteServer.foreignDataWrapperId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!currentForeignTableNames.includes(remoteTableLocalName)) {
|
||||||
|
throw new Error('Remote table does not exist');
|
||||||
|
}
|
||||||
|
|
||||||
|
const objectMetadata =
|
||||||
|
await this.objectMetadataService.findOneWithinWorkspace(workspaceId, {
|
||||||
|
where: { nameSingular: remoteTableLocalName },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (objectMetadata) {
|
||||||
|
await this.objectMetadataService.deleteOneObject(
|
||||||
|
{ id: objectMetadata.id },
|
||||||
|
workspaceId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.workspaceMigrationService.createCustomMigration(
|
||||||
|
generateMigrationName(`drop-foreign-table-${remoteTableLocalName}`),
|
||||||
|
workspaceId,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name: remoteTableLocalName,
|
||||||
|
action: WorkspaceMigrationTableActionType.DROP_FOREIGN_TABLE,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations(
|
||||||
workspaceId,
|
workspaceId,
|
||||||
);
|
);
|
||||||
|
|
||||||
await this.workspaceCacheVersionService.incrementVersion(workspaceId);
|
await this.workspaceCacheVersionService.incrementVersion(workspaceId);
|
||||||
|
|
||||||
return remoteTable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async createForeignTableAndMetadata(
|
private async createForeignTableAndMetadata(
|
||||||
@ -133,9 +211,14 @@ export class RemoteTableService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const currentForeignTableNames =
|
const currentForeignTableNames =
|
||||||
await this.fetchForeignTableNamesWithinWorkspace(workspaceId);
|
await this.fetchForeignTableNamesWithinWorkspace(
|
||||||
|
workspaceId,
|
||||||
|
remoteServer.foreignDataWrapperId,
|
||||||
|
);
|
||||||
|
|
||||||
if (currentForeignTableNames.includes(getRemoteTableName(input.name))) {
|
if (
|
||||||
|
currentForeignTableNames.includes(getRemoteTableLocalName(input.name))
|
||||||
|
) {
|
||||||
throw new Error('Remote table already exists');
|
throw new Error('Remote table already exists');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,8 +228,8 @@ export class RemoteTableService {
|
|||||||
input.schema,
|
input.schema,
|
||||||
);
|
);
|
||||||
|
|
||||||
const remoteTableName = getRemoteTableName(input.name);
|
const remoteTableLocalName = getRemoteTableLocalName(input.name);
|
||||||
const remoteTableLabel = camelToTitleCase(remoteTableName);
|
const remoteTableLabel = camelToTitleCase(remoteTableLocalName);
|
||||||
|
|
||||||
// We only support remote tables with an id column for now.
|
// We only support remote tables with an id column for now.
|
||||||
const remoteTableIdColumn = remoteTableColumns.filter(
|
const remoteTableIdColumn = remoteTableColumns.filter(
|
||||||
@ -163,11 +246,11 @@ export class RemoteTableService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await this.workspaceMigrationService.createCustomMigration(
|
await this.workspaceMigrationService.createCustomMigration(
|
||||||
generateMigrationName(`create-foreign-table-${remoteTableName}`),
|
generateMigrationName(`create-foreign-table-${remoteTableLocalName}`),
|
||||||
workspaceId,
|
workspaceId,
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
name: remoteTableName,
|
name: remoteTableLocalName,
|
||||||
action: WorkspaceMigrationTableActionType.CREATE_FOREIGN_TABLE,
|
action: WorkspaceMigrationTableActionType.CREATE_FOREIGN_TABLE,
|
||||||
foreignTable: {
|
foreignTable: {
|
||||||
columns: remoteTableColumns.map(
|
columns: remoteTableColumns.map(
|
||||||
@ -190,8 +273,8 @@ export class RemoteTableService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const objectMetadata = await this.objectMetadataService.createOne({
|
const objectMetadata = await this.objectMetadataService.createOne({
|
||||||
nameSingular: remoteTableName,
|
nameSingular: remoteTableLocalName,
|
||||||
namePlural: `${remoteTableName}s`,
|
namePlural: `${remoteTableLocalName}s`,
|
||||||
labelSingular: remoteTableLabel,
|
labelSingular: remoteTableLabel,
|
||||||
labelPlural: `${remoteTableLabel}s`,
|
labelPlural: `${remoteTableLabel}s`,
|
||||||
description: 'Remote table',
|
description: 'Remote table',
|
||||||
@ -223,6 +306,8 @@ export class RemoteTableService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.workspaceCacheVersionService.incrementVersion(workspaceId);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: input.name,
|
name: input.name,
|
||||||
schema: input.schema,
|
schema: input.schema,
|
||||||
@ -230,53 +315,6 @@ export class RemoteTableService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async removeForeignTableAndMetadata(
|
|
||||||
input: RemoteTableInput,
|
|
||||||
workspaceId: string,
|
|
||||||
): Promise<RemoteTableDTO> {
|
|
||||||
const remoteTableName = getRemoteTableName(input.name);
|
|
||||||
|
|
||||||
const currentForeignTableNames =
|
|
||||||
await this.fetchForeignTableNamesWithinWorkspace(workspaceId);
|
|
||||||
|
|
||||||
if (!currentForeignTableNames.includes(remoteTableName)) {
|
|
||||||
throw new Error('Remote table does not exist');
|
|
||||||
}
|
|
||||||
|
|
||||||
const objectMetadata =
|
|
||||||
await this.objectMetadataService.findOneWithinWorkspace(workspaceId, {
|
|
||||||
where: { nameSingular: remoteTableName },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (objectMetadata) {
|
|
||||||
await this.objectMetadataService.deleteOneObject(
|
|
||||||
{ id: objectMetadata.id },
|
|
||||||
workspaceId,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.workspaceMigrationService.createCustomMigration(
|
|
||||||
generateMigrationName(`drop-foreign-table-${input.name}`),
|
|
||||||
workspaceId,
|
|
||||||
[
|
|
||||||
{
|
|
||||||
name: remoteTableName,
|
|
||||||
action: WorkspaceMigrationTableActionType.DROP_FOREIGN_TABLE,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.workspaceMigrationRunnerService.executeMigrationFromPendingMigrations(
|
|
||||||
workspaceId,
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: input.name,
|
|
||||||
schema: input.schema,
|
|
||||||
status: RemoteTableStatus.NOT_SYNCED,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private async fetchTableColumnsSchema(
|
private async fetchTableColumnsSchema(
|
||||||
remoteServer: RemoteServerEntity<RemoteServerType>,
|
remoteServer: RemoteServerEntity<RemoteServerType>,
|
||||||
tableName: string,
|
tableName: string,
|
||||||
@ -299,19 +337,6 @@ export class RemoteTableService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async fetchForeignTableNamesWithinWorkspace(workspaceId: string) {
|
|
||||||
const workspaceDataSource =
|
|
||||||
await this.workspaceDataSourceService.connectToWorkspaceDataSource(
|
|
||||||
workspaceId,
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
await workspaceDataSource.query(
|
|
||||||
`SELECT foreign_table_name FROM information_schema.foreign_tables`,
|
|
||||||
)
|
|
||||||
).map((foreignTable) => foreignTable.foreign_table_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async fetchTablesFromRemoteSchema(
|
private async fetchTablesFromRemoteSchema(
|
||||||
remoteServer: RemoteServerEntity<RemoteServerType>,
|
remoteServer: RemoteServerEntity<RemoteServerType>,
|
||||||
): Promise<RemoteTable[]> {
|
): Promise<RemoteTable[]> {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { camelCase } from 'src/utils/camel-case';
|
import { camelCase } from 'src/utils/camel-case';
|
||||||
|
|
||||||
export const getRemoteTableName = (distantTableName: string) =>
|
export const getRemoteTableLocalName = (distantTableName: string) =>
|
||||||
`${camelCase(distantTableName)}Remote`;
|
`${camelCase(distantTableName)}Remote`;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
const INPUT_REGEX = /^([A-Za-z0-9\-\_\.]+)$/;
|
const INPUT_REGEX = /^([A-Za-z0-9\-_.@]+)$/;
|
||||||
|
|
||||||
export const validateObject = (input: object) => {
|
export const validateObject = (input: object) => {
|
||||||
for (const [key, value] of Object.entries(input)) {
|
for (const [key, value] of Object.entries(input)) {
|
||||||
|
|||||||
@ -88,7 +88,7 @@ export enum WorkspaceMigrationTableActionType {
|
|||||||
ALTER = 'alter',
|
ALTER = 'alter',
|
||||||
DROP = 'drop',
|
DROP = 'drop',
|
||||||
CREATE_FOREIGN_TABLE = 'create_foreign_table',
|
CREATE_FOREIGN_TABLE = 'create_foreign_table',
|
||||||
DROP_FOREIGN_TABLE = 'drop_foreign_table'
|
DROP_FOREIGN_TABLE = 'drop_foreign_table',
|
||||||
}
|
}
|
||||||
|
|
||||||
export type WorkspaceMigrationTableAction = {
|
export type WorkspaceMigrationTableAction = {
|
||||||
|
|||||||
@ -38,7 +38,6 @@ export const EntityChip = ({
|
|||||||
maxWidth,
|
maxWidth,
|
||||||
}: EntityChipProps) => {
|
}: EntityChipProps) => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const handleLinkClick = (event: React.MouseEvent<HTMLDivElement>) => {
|
const handleLinkClick = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user