diff --git a/server/bundles/io.cloudbeaver.service.data.transfer/schema/service.data.transfer.graphqls b/server/bundles/io.cloudbeaver.service.data.transfer/schema/service.data.transfer.graphqls index 3fd450d4299..9b2231c8e0e 100644 --- a/server/bundles/io.cloudbeaver.service.data.transfer/schema/service.data.transfer.graphqls +++ b/server/bundles/io.cloudbeaver.service.data.transfer/schema/service.data.transfer.graphqls @@ -48,6 +48,8 @@ input DataTransferImportSettings { useBulkLoad: Boolean "Use transactions during import" useTransactions: Boolean + "Open new connection(s) for the import task" + openNewConnection: Boolean } input DataTransferImportParameters { diff --git a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/impl/WebServiceDataTransfer.java b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/impl/WebServiceDataTransfer.java index 86a93c4896c..32b2672ac3b 100644 --- a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/impl/WebServiceDataTransfer.java +++ b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/impl/WebServiceDataTransfer.java @@ -425,7 +425,7 @@ private void importData( throw new DBWebException("Import is canceled"); } } catch (DBException e) { - throw new DBWebException("Import failed cause: " + e.getMessage()); + throw new DBWebException("Import failed", e); } } } @@ -446,6 +446,9 @@ private void applyImportSettings( consumerSettings.setUseTransactions(CommonUtils.getBoolean( settings.get(DTConstants.PROP_USE_TRANSACTIONS), consumerSettings.isUseTransactions())); + consumerSettings.setOpenNewConnections(CommonUtils.getBoolean( + settings.get(DTConstants.PROP_OPEN_NEW_CONNECTION), + consumerSettings.isOpenNewConnections())); } } diff --git a/webapp/packages/core-sdk/src/queries/connections/getConnectionImportDriverConfiguration.gql b/webapp/packages/core-sdk/src/queries/connections/getConnectionImportDriverConfiguration.gql new file mode 100644 index 00000000000..c2c51be19eb --- /dev/null +++ b/webapp/packages/core-sdk/src/queries/connections/getConnectionImportDriverConfiguration.gql @@ -0,0 +1,9 @@ +query getConnectionImportDriverConfiguration($projectId: ID, $connectionId: ID!) { + connections: userConnections(projectId: $projectId, id: $connectionId) { + id + projectId + driverConfiguration { + ...DriverConfiguration + } + } +} diff --git a/webapp/packages/core-sdk/src/queries/fragments/DriverConfiguration.gql b/webapp/packages/core-sdk/src/queries/fragments/DriverConfiguration.gql new file mode 100644 index 00000000000..7366bfc8cc4 --- /dev/null +++ b/webapp/packages/core-sdk/src/queries/fragments/DriverConfiguration.gql @@ -0,0 +1,9 @@ +fragment DriverConfiguration on DriverConfiguration { + supportedInsertReplaceMethods { + id + name + description + } + supportsBulkLoad + supportsTransactions +} diff --git a/webapp/packages/plugin-data-import/package.json b/webapp/packages/plugin-data-import/package.json index 88119f2dfe8..6c8d1018694 100644 --- a/webapp/packages/plugin-data-import/package.json +++ b/webapp/packages/plugin-data-import/package.json @@ -23,6 +23,7 @@ }, "dependencies": { "@cloudbeaver/core-blocks": "workspace:*", + "@cloudbeaver/core-connections": "workspace:*", "@cloudbeaver/core-di": "workspace:*", "@cloudbeaver/core-dialogs": "workspace:*", "@cloudbeaver/core-events": "workspace:*", diff --git a/webapp/packages/plugin-data-import/src/DataImportBootstrap.ts b/webapp/packages/plugin-data-import/src/DataImportBootstrap.ts index e881c96352e..44e40e8e933 100644 --- a/webapp/packages/plugin-data-import/src/DataImportBootstrap.ts +++ b/webapp/packages/plugin-data-import/src/DataImportBootstrap.ts @@ -5,6 +5,7 @@ * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. */ +import { createConnectionParam } from '@cloudbeaver/core-connections'; import { Bootstrap, injectable } from '@cloudbeaver/core-di'; import { CommonDialogService, DialogueStateResult } from '@cloudbeaver/core-dialogs'; import { ACTION_IMPORT, ActionService, menuExtractItems, MenuService } from '@cloudbeaver/core-view'; @@ -72,16 +73,20 @@ export class DataImportBootstrap extends Bootstrap { throw new Error('Execution context must be provided'); } - const { status, result: dialogResult } = await this.commonDialogService.open(DataImportDialogLazy, { tableName: model.name ?? model.id }); + const connectionKey = createConnectionParam(executionContext.projectId, executionContext.connectionId); + const { status, result: dialogResult } = await this.commonDialogService.open(DataImportDialogLazy, { + tableName: model.name ?? model.id, + connectionKey, + }); if (status === DialogueStateResult.Resolved && dialogResult) { const success = await this.dataImportService.importData( - executionContext.connectionId, + connectionKey, executionContext.id, - executionContext.projectId, result.id, dialogResult.processorId, dialogResult.file, + dialogResult.settings, ); if (success) { diff --git a/webapp/packages/plugin-data-import/src/DataImportDialog/DataImportDialog.tsx b/webapp/packages/plugin-data-import/src/DataImportDialog/DataImportDialog.tsx index ec9753f8289..f6dc94f096c 100644 --- a/webapp/packages/plugin-data-import/src/DataImportDialog/DataImportDialog.tsx +++ b/webapp/packages/plugin-data-import/src/DataImportDialog/DataImportDialog.tsx @@ -1,28 +1,34 @@ /* * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2025 DBeaver Corp and others + * Copyright (C) 2020-2026 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. */ import { observer } from 'mobx-react-lite'; -import { Button, CommonDialogBody, CommonDialogFooter, CommonDialogHeader, CommonDialogWrapper, useTranslate } from '@cloudbeaver/core-blocks'; +import { Button, CommonDialogBody, CommonDialogFooter, CommonDialogHeader, CommonDialogWrapper, useResource, useTranslate } from '@cloudbeaver/core-blocks'; +import type { IConnectionInfoParams } from '@cloudbeaver/core-connections'; import type { DialogComponent } from '@cloudbeaver/core-dialogs'; +import type { DataTransferImportSettings } from '@cloudbeaver/core-sdk'; +import { DataImportDriverConfigurationResource } from '../DataImportDriverConfigurationResource.js'; import { DataImportFileSelector } from './DataImportFileSelector.js'; import { EDataImportDialogStep } from './EDataImportDialogStep.js'; import type { IDataImportDialogState } from './IDataImportDialogState.js'; import { ImportProcessorList } from './ImportProcessorList.js'; +import { ImportSettingsForm } from './ImportSettingsForm.js'; import { useDataImportDialog } from './useDataImportDialog.js'; export interface IDataImportDialogResult { file: File; processorId: string; + settings: DataTransferImportSettings; } export interface IDataImportDialogPayload { tableName: string; + connectionKey: IConnectionInfoParams; initialState?: IDataImportDialogState; } @@ -33,21 +39,51 @@ export const DataImportDialog: DialogComponent 0); let title = translate('plugin_data_import_title'); let icon = '/icons/data-import.svg'; - if (dialog.state.step === EDataImportDialogStep.File && dialog.state.selectedProcessor) { + if (dialog.state.step !== EDataImportDialogStep.Processor && dialog.state.selectedProcessor) { title += ` (${dialog.state.selectedProcessor.name ?? dialog.state.selectedProcessor.id})`; icon = dialog.state.selectedProcessor.icon ?? icon; } + function importData() { + if (dialog.state.file && dialog.state.selectedProcessor) { + resolveDialog({ file: dialog.state.file, processorId: dialog.state.selectedProcessor.id, settings: dialog.state.settings }); + } + } + + function submitFileStep() { + if (driverConfiguration && hasSettings) { + dialog.goToSettings(driverConfiguration); + } else { + importData(); + } + } + return ( {dialog.state.step === EDataImportDialogStep.Processor && } {dialog.state.step === EDataImportDialogStep.File && } + {dialog.state.step === EDataImportDialogStep.Settings && driverConfiguration && ( + + )} @@ -61,9 +97,20 @@ export const DataImportDialog: DialogComponent + + )} + {dialog.state.step === EDataImportDialogStep.Settings && ( +
+ +
diff --git a/webapp/packages/plugin-data-import/src/DataImportDialog/EDataImportDialogStep.ts b/webapp/packages/plugin-data-import/src/DataImportDialog/EDataImportDialogStep.ts index e4385692bd2..7ac8b41052d 100644 --- a/webapp/packages/plugin-data-import/src/DataImportDialog/EDataImportDialogStep.ts +++ b/webapp/packages/plugin-data-import/src/DataImportDialog/EDataImportDialogStep.ts @@ -1,6 +1,6 @@ /* * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2024 DBeaver Corp and others + * Copyright (C) 2020-2026 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. @@ -8,4 +8,5 @@ export enum EDataImportDialogStep { Processor, File, + Settings, } diff --git a/webapp/packages/plugin-data-import/src/DataImportDialog/IDataImportDialogState.ts b/webapp/packages/plugin-data-import/src/DataImportDialog/IDataImportDialogState.ts index 2631efb81c0..785becd6d51 100644 --- a/webapp/packages/plugin-data-import/src/DataImportDialog/IDataImportDialogState.ts +++ b/webapp/packages/plugin-data-import/src/DataImportDialog/IDataImportDialogState.ts @@ -1,11 +1,11 @@ /* * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2024 DBeaver Corp and others + * Copyright (C) 2020-2026 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. */ -import type { DataTransferProcessorInfo } from '@cloudbeaver/core-sdk'; +import type { DataTransferImportSettings, DataTransferProcessorInfo } from '@cloudbeaver/core-sdk'; import type { EDataImportDialogStep } from './EDataImportDialogStep.js'; @@ -13,4 +13,5 @@ export interface IDataImportDialogState { step: EDataImportDialogStep; file: File | null; selectedProcessor: DataTransferProcessorInfo | null; + settings: DataTransferImportSettings; } diff --git a/webapp/packages/plugin-data-import/src/DataImportDialog/ImportSettingsForm.tsx b/webapp/packages/plugin-data-import/src/DataImportDialog/ImportSettingsForm.tsx new file mode 100644 index 00000000000..663794b4372 --- /dev/null +++ b/webapp/packages/plugin-data-import/src/DataImportDialog/ImportSettingsForm.tsx @@ -0,0 +1,72 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +import { observer } from 'mobx-react-lite'; +import { useEffect } from 'react'; + +import { Combobox, Container, FieldCheckbox, Link, useTranslate } from '@cloudbeaver/core-blocks'; +import type { DataTransferImportSettings } from '@cloudbeaver/core-sdk'; + +import type { IDataImportDriverConfiguration } from '../DataImportDriverConfigurationResource.js'; + +interface Props { + settings: DataTransferImportSettings; + driverConfiguration: IDataImportDriverConfiguration; +} + +export const ImportSettingsForm = observer(function ImportSettingsForm({ settings, driverConfiguration }) { + const translate = useTranslate(); + const { supportedInsertReplaceMethods, supportsBulkLoad, supportsTransactions } = driverConfiguration; + + useEffect(() => { + if (settings.useBulkLoad) { + settings.onDuplicateKeyMethod = undefined; + } + }, [settings.useBulkLoad]); + + return ( + + {supportedInsertReplaceMethods.length > 0 && ( +
+ method.id} + valueSelector={method => method.name} + titleSelector={method => method.description ?? undefined} + disabled={settings.useBulkLoad} + title={translate('plugin_data_import_settings_on_duplicate_key_title')} + placeholder={translate('plugin_data_import_settings_on_duplicate_key_placeholder')} + > + {translate('plugin_data_import_settings_on_duplicate_key')} + + + {translate('plugin_data_import_settings_on_duplicate_key_help')} + +
+ )} + {(supportsBulkLoad || supportsTransactions) && ( + + {supportsBulkLoad && ( + + {translate('plugin_data_import_settings_use_bulk_load')} + + )} + {supportsTransactions && ( + + {translate('plugin_data_import_settings_use_transactions')} + + )} + + {translate('plugin_data_import_settings_open_new_connection')} + + + )} +
+ ); +}); diff --git a/webapp/packages/plugin-data-import/src/DataImportDialog/useDataImportDialog.ts b/webapp/packages/plugin-data-import/src/DataImportDialog/useDataImportDialog.ts index 6ad0c6a5875..7b026db28d9 100644 --- a/webapp/packages/plugin-data-import/src/DataImportDialog/useDataImportDialog.ts +++ b/webapp/packages/plugin-data-import/src/DataImportDialog/useDataImportDialog.ts @@ -1,6 +1,6 @@ /* * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2024 DBeaver Corp and others + * Copyright (C) 2020-2026 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. @@ -8,14 +8,16 @@ import { action, observable } from 'mobx'; import { useObservableRef } from '@cloudbeaver/core-blocks'; -import type { DataTransferProcessorInfo } from '@cloudbeaver/core-sdk'; +import type { DataTransferImportSettings, DataTransferProcessorInfo } from '@cloudbeaver/core-sdk'; +import type { IDataImportDriverConfiguration } from '../DataImportDriverConfigurationResource.js'; import { EDataImportDialogStep } from './EDataImportDialogStep.js'; import type { IDataImportDialogState } from './IDataImportDialogState.js'; interface IDialog { state: IDataImportDialogState; stepBack: () => void; + goToSettings: (configuration: IDataImportDriverConfiguration) => void; selectProcessor: (processor: DataTransferProcessorInfo) => void; deleteFile: () => void; reset: () => void; @@ -25,17 +27,44 @@ const DEFAULT_STATE_GETTER: () => IDataImportDialogState = () => ({ step: EDataImportDialogStep.Processor, file: null, selectedProcessor: null, + settings: {}, }); -export function useDataImportDialog(initialState?: IDataImportDialogState) { - const dialog = useObservableRef( +function getDefaultSettings(configuration: IDataImportDriverConfiguration): DataTransferImportSettings { + const settings: DataTransferImportSettings = { + openNewConnection: true, + }; + + if (configuration.supportsTransactions) { + settings.useTransactions = true; + } + + if (configuration.supportedInsertReplaceMethods) { + settings.onDuplicateKeyMethod = undefined; + } + + if (configuration.supportsBulkLoad) { + settings.useBulkLoad = false; + } + + return settings; +} + +export function useDataImportDialog(initialState?: IDataImportDialogState): IDialog { + return useObservableRef( () => ({ state: initialState ?? DEFAULT_STATE_GETTER(), stepBack() { - if (this.state.step === EDataImportDialogStep.File) { + if (this.state.step === EDataImportDialogStep.Settings) { + this.state.step = EDataImportDialogStep.File; + } else if (this.state.step === EDataImportDialogStep.File) { this.state.step = EDataImportDialogStep.Processor; } }, + goToSettings(configuration: IDataImportDriverConfiguration) { + this.state.settings = { ...getDefaultSettings(configuration), ...this.state.settings }; + this.state.step = EDataImportDialogStep.Settings; + }, selectProcessor(processor: DataTransferProcessorInfo) { if (this.state.selectedProcessor && this.state.selectedProcessor.id !== processor.id) { this.reset(); @@ -51,9 +80,14 @@ export function useDataImportDialog(initialState?: IDataImportDialogState) { this.state = DEFAULT_STATE_GETTER(); }, }), - { state: observable, stepBack: action.bound, selectProcessor: action.bound, deleteFile: action.bound, reset: action.bound }, + { + state: observable, + stepBack: action.bound, + goToSettings: action.bound, + selectProcessor: action.bound, + deleteFile: action.bound, + reset: action.bound, + }, false, ); - - return dialog; } diff --git a/webapp/packages/plugin-data-import/src/DataImportDriverConfigurationResource.ts b/webapp/packages/plugin-data-import/src/DataImportDriverConfigurationResource.ts new file mode 100644 index 00000000000..2ea34100dd6 --- /dev/null +++ b/webapp/packages/plugin-data-import/src/DataImportDriverConfigurationResource.ts @@ -0,0 +1,79 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +import { toJS } from 'mobx'; + +import { + CONNECTION_INFO_PARAM_SCHEMA, + ConnectionInfoResource, + createConnectionParam, + type IConnectionInfoParams, + isConnectionInfoParamEqual, +} from '@cloudbeaver/core-connections'; +import { injectable } from '@cloudbeaver/core-di'; +import { CachedMapResource, isResourceAlias, type ResourceKey, resourceKeyList, ResourceKeyUtils } from '@cloudbeaver/core-resource'; +import { type DriverConfigurationFragment, type GetConnectionImportDriverConfigurationQuery, GraphQLService } from '@cloudbeaver/core-sdk'; +import { schemaValidationError } from '@cloudbeaver/core-utils'; + +export type IDataImportDriverConfiguration = DriverConfigurationFragment; + +@injectable(() => [GraphQLService, ConnectionInfoResource]) +export class DataImportDriverConfigurationResource extends CachedMapResource { + constructor( + private readonly graphQLService: GraphQLService, + private readonly connectionInfoResource: ConnectionInfoResource, + ) { + super(); + + this.sync(connectionInfoResource); + connectionInfoResource.onItemDelete.addHandler(this.delete.bind(this)); + } + + override isKeyEqual(param: IConnectionInfoParams, second: IConnectionInfoParams): boolean { + return isConnectionInfoParamEqual(param, second); + } + + protected async loader(originalKey: ResourceKey): Promise> { + if (isResourceAlias(originalKey)) { + throw new Error('Aliases are not supported by this resource'); + } + + const connectionsList: GetConnectionImportDriverConfigurationQuery['connections'] = []; + + await ResourceKeyUtils.forEachAsync(originalKey, async key => { + if (!this.connectionInfoResource.isConnected(key)) { + throw new Error(`Connection is not connected (${key.projectId}, ${key.connectionId})`); + } + + const { connections } = await this.graphQLService.sdk.getConnectionImportDriverConfiguration({ + projectId: key.projectId, + connectionId: key.connectionId, + }); + + if (!connections.some(connection => connection.id === key.connectionId)) { + throw new Error(`Connection is not found (${key.connectionId})`); + } + + connectionsList.push(...connections); + }); + + this.set( + resourceKeyList(connectionsList.map(createConnectionParam)), + connectionsList.map(connection => connection.driverConfiguration), + ); + + return this.data; + } + + protected validateKey(key: IConnectionInfoParams): boolean { + const parse = CONNECTION_INFO_PARAM_SCHEMA.safeParse(toJS(key)); + if (!parse.success) { + this.logger.warn(`Invalid resource key ${schemaValidationError(parse.error).toString()}`); + } + return parse.success; + } +} diff --git a/webapp/packages/plugin-data-import/src/DataImportService.ts b/webapp/packages/plugin-data-import/src/DataImportService.ts index f6bb8e412a6..27d2593b4a0 100644 --- a/webapp/packages/plugin-data-import/src/DataImportService.ts +++ b/webapp/packages/plugin-data-import/src/DataImportService.ts @@ -8,10 +8,11 @@ import { computed, makeObservable } from 'mobx'; import { ProcessSnackbar } from '@cloudbeaver/core-blocks'; +import type { IConnectionInfoParams } from '@cloudbeaver/core-connections'; import { injectable } from '@cloudbeaver/core-di'; import { NotificationService } from '@cloudbeaver/core-events'; import { AsyncTaskInfoService, EAdminPermission, SessionPermissionsResource } from '@cloudbeaver/core-root'; -import { GraphQLService } from '@cloudbeaver/core-sdk'; +import { type DataTransferImportSettings, GraphQLService } from '@cloudbeaver/core-sdk'; import { getProgressPercent } from '@cloudbeaver/core-utils'; import { DataImportSettingsService } from './DataImportSettingsService.js'; @@ -38,7 +39,14 @@ export class DataImportService { }); } - async importData(connectionId: string, contextId: string, projectId: string, resultsId: string, processorId: string, file: File): Promise { + async importData( + connectionKey: IConnectionInfoParams, + contextId: string, + resultsId: string, + processorId: string, + file: File, + settings?: DataTransferImportSettings, + ): Promise { const abortController = new AbortController(); let cancelImplementation: (() => void | Promise) | null; let isCancelled = false; @@ -65,11 +73,11 @@ export class DataImportService { const { taskInfo } = await this.graphQLService.sdk.dataTransferImportDataIntoResults( { - connectionId, contextId, - projectId, + connectionId: connectionKey.connectionId, + projectId: connectionKey.projectId, resultsId, - parameters: { processorId }, + parameters: { processorId, settings }, }, undefined, abortController.signal, diff --git a/webapp/packages/plugin-data-import/src/locales/en.ts b/webapp/packages/plugin-data-import/src/locales/en.ts index 3c505d522fb..48c7c4d19a3 100644 --- a/webapp/packages/plugin-data-import/src/locales/en.ts +++ b/webapp/packages/plugin-data-import/src/locales/en.ts @@ -4,4 +4,21 @@ export default [ ['plugin_data_import_process_success', 'Data imported successfully'], ['plugin_data_import_process_fail', 'Data import failed'], ['plugin_data_import_process_file_processing_step_message', 'File uploaded, processing...'], + ['plugin_data_import_settings_on_duplicate_key', 'On duplicate key'], + ['plugin_data_import_settings_use_bulk_load', 'Use bulk load'], + ['plugin_data_import_settings_use_bulk_load_title', 'Use bulk load for faster import'], + ['plugin_data_import_settings_use_transactions', 'Use transactions'], + [ + 'plugin_data_import_settings_use_transactions_title', + 'Execute the import within a database transaction. If the import fails, all changes can be rolled back', + ], + + [ + 'plugin_data_import_settings_on_duplicate_key_title', + 'Choose how to handle rows that already exist in the target table based on a primary or unique key', + ], + ['plugin_data_import_settings_on_duplicate_key_placeholder', 'None'], + ['plugin_data_import_settings_open_new_connection', 'Open new connection'], + ['plugin_data_import_settings_open_new_connection_title', 'Open a new connection after the import is complete'], + ['plugin_data_import_settings_on_duplicate_key_help', 'Learn more about the "On duplicate key" setting'], ]; diff --git a/webapp/packages/plugin-data-import/src/locales/fr.ts b/webapp/packages/plugin-data-import/src/locales/fr.ts index ea7dd3ef6c4..900a8ac9bb8 100644 --- a/webapp/packages/plugin-data-import/src/locales/fr.ts +++ b/webapp/packages/plugin-data-import/src/locales/fr.ts @@ -4,4 +4,20 @@ export default [ ['plugin_data_import_process_success', 'Données importées avec succès'], ['plugin_data_import_process_fail', "Échec de l'importation des données"], ['plugin_data_import_process_file_processing_step_message', 'Fichier téléchargé, en cours de traitement...'], + ['plugin_data_import_settings_on_duplicate_key', 'En cas de clé dupliquée'], + [ + 'plugin_data_import_settings_on_duplicate_key_title', + "Choisissez comment gérer les lignes qui existent déjà dans la table cible en fonction d'une clé primaire ou unique", + ], + ['plugin_data_import_settings_use_bulk_load_title', 'Utiliser le chargement en masse pour un import plus rapide'], + ['plugin_data_import_settings_use_bulk_load', 'Utiliser le chargement en masse'], + ['plugin_data_import_settings_use_transactions', 'Utiliser les transactions'], + [ + 'plugin_data_import_settings_use_transactions_title', + "Exécuter l'importation dans une transaction de base de données. Si l'importation échoue, toutes les modifications peuvent être annulées", + ], + ['plugin_data_import_settings_on_duplicate_key_placeholder', 'Aucune'], + ['plugin_data_import_settings_open_new_connection', 'Ouvrir une nouvelle connexion'], + ['plugin_data_import_settings_open_new_connection_title', "Ouvrir une nouvelle connexion après l'importation"], + ['plugin_data_import_settings_on_duplicate_key_help', 'En savoir plus sur le paramètre "En cas de clé dupliquée"'], ]; diff --git a/webapp/packages/plugin-data-import/src/locales/it.ts b/webapp/packages/plugin-data-import/src/locales/it.ts index 3c505d522fb..48c7c4d19a3 100644 --- a/webapp/packages/plugin-data-import/src/locales/it.ts +++ b/webapp/packages/plugin-data-import/src/locales/it.ts @@ -4,4 +4,21 @@ export default [ ['plugin_data_import_process_success', 'Data imported successfully'], ['plugin_data_import_process_fail', 'Data import failed'], ['plugin_data_import_process_file_processing_step_message', 'File uploaded, processing...'], + ['plugin_data_import_settings_on_duplicate_key', 'On duplicate key'], + ['plugin_data_import_settings_use_bulk_load', 'Use bulk load'], + ['plugin_data_import_settings_use_bulk_load_title', 'Use bulk load for faster import'], + ['plugin_data_import_settings_use_transactions', 'Use transactions'], + [ + 'plugin_data_import_settings_use_transactions_title', + 'Execute the import within a database transaction. If the import fails, all changes can be rolled back', + ], + + [ + 'plugin_data_import_settings_on_duplicate_key_title', + 'Choose how to handle rows that already exist in the target table based on a primary or unique key', + ], + ['plugin_data_import_settings_on_duplicate_key_placeholder', 'None'], + ['plugin_data_import_settings_open_new_connection', 'Open new connection'], + ['plugin_data_import_settings_open_new_connection_title', 'Open a new connection after the import is complete'], + ['plugin_data_import_settings_on_duplicate_key_help', 'Learn more about the "On duplicate key" setting'], ]; diff --git a/webapp/packages/plugin-data-import/src/locales/ru.ts b/webapp/packages/plugin-data-import/src/locales/ru.ts index 7e08b40f551..dcb447b4094 100644 --- a/webapp/packages/plugin-data-import/src/locales/ru.ts +++ b/webapp/packages/plugin-data-import/src/locales/ru.ts @@ -4,4 +4,21 @@ export default [ ['plugin_data_import_process_success', 'Данные успешно импортированы'], ['plugin_data_import_process_fail', 'Ошибка импорта данных'], ['plugin_data_import_process_file_processing_step_message', 'Файл загружен, обработка...'], + ['plugin_data_import_settings_on_duplicate_key', 'При дублировании ключа'], + [ + 'plugin_data_import_settings_on_duplicate_key_title', + 'Выберите, как обрабатывать строки, которые уже существуют в целевой таблице на основе первичного или уникального ключа', + ], + + ['plugin_data_import_settings_use_bulk_load', 'Использовать массовую загрузку'], + ['plugin_data_import_settings_use_bulk_load_title', 'Использовать массовую загрузку для более быстрого импорта'], + ['plugin_data_import_settings_use_transactions', 'Использовать транзакции'], + [ + 'plugin_data_import_settings_use_transactions_title', + 'Выполнять импорт в рамках транзакции базы данных. Если импорт не удается, все изменения могут быть отменены', + ], + ['plugin_data_import_settings_on_duplicate_key_placeholder', 'Нет'], + ['plugin_data_import_settings_open_new_connection', 'Открыть новое соединение'], + ['plugin_data_import_settings_open_new_connection_title', 'Открыть новое соединение после завершения импорта'], + ['plugin_data_import_settings_on_duplicate_key_help', 'Узнать больше о параметре "При дублировании ключа"'], ]; diff --git a/webapp/packages/plugin-data-import/src/locales/vi.ts b/webapp/packages/plugin-data-import/src/locales/vi.ts index 940a3e7a08d..2a8e07ddfbc 100644 --- a/webapp/packages/plugin-data-import/src/locales/vi.ts +++ b/webapp/packages/plugin-data-import/src/locales/vi.ts @@ -4,4 +4,20 @@ export default [ ['plugin_data_import_process_success', 'Dữ liệu đã được nhập thành công'], ['plugin_data_import_process_fail', 'Nhập dữ liệu thất bại'], ['plugin_data_import_process_file_processing_step_message', 'Tệp đã được tải lên, đang xử lý...'], + ['plugin_data_import_settings_on_duplicate_key', 'Khi trùng lặp khóa'], + [ + 'plugin_data_import_settings_on_duplicate_key_title', + 'Chọn cách xử lý các hàng đã tồn tại trong bảng đích dựa trên khóa chính hoặc khóa duy nhất', + ], + ['plugin_data_import_settings_use_bulk_load', 'Sử dụng tải hàng loạt'], + ['plugin_data_import_settings_use_bulk_load_title', 'Sử dụng tải hàng loạt để nhập nhanh hơn'], + ['plugin_data_import_settings_use_transactions', 'Sử dụng giao dịch'], + [ + 'plugin_data_import_settings_use_transactions_title', + 'Thực hiện nhập trong một giao dịch cơ sở dữ liệu. Nếu nhập thất bại, tất cả các thay đổi có thể được hoàn tác', + ], + ['plugin_data_import_settings_on_duplicate_key_placeholder', 'Không có'], + ['plugin_data_import_settings_open_new_connection', 'Mở kết nối mới'], + ['plugin_data_import_settings_open_new_connection_title', 'Mở kết nối mới sau khi nhập hoàn tất'], + ['plugin_data_import_settings_on_duplicate_key_help', 'Tìm hiểu thêm về cài đặt "Khi trùng lặp khóa"'], ]; diff --git a/webapp/packages/plugin-data-import/src/locales/zh.ts b/webapp/packages/plugin-data-import/src/locales/zh.ts index f39b85c3b9f..cbdeafa4a31 100644 --- a/webapp/packages/plugin-data-import/src/locales/zh.ts +++ b/webapp/packages/plugin-data-import/src/locales/zh.ts @@ -4,4 +4,14 @@ export default [ ['plugin_data_import_process_success', '数据导入成功'], ['plugin_data_import_process_fail', '数据导入失败'], ['plugin_data_import_process_file_processing_step_message', '文件已上传,正在处理...'], + ['plugin_data_import_settings_on_duplicate_key', '当重复键时'], + ['plugin_data_import_settings_on_duplicate_key_title', '选择如何处理目标表中已存在的行,基于主键或唯一键'], + ['plugin_data_import_settings_use_bulk_load', '使用批量加载'], + ['plugin_data_import_settings_use_bulk_load_title', '使用批量加载以更快地导入数据'], + ['plugin_data_import_settings_use_transactions', '使用事务'], + ['plugin_data_import_settings_use_transactions_title', '在数据库事务中执行导入。如果导入失败,所有更改都可以回滚'], + ['plugin_data_import_settings_on_duplicate_key_placeholder', '无'], + ['plugin_data_import_settings_open_new_connection', '打开新连接'], + ['plugin_data_import_settings_open_new_connection_title', '导入完成后打开新连接'], + ['plugin_data_import_settings_on_duplicate_key_help', '了解有关“当重复键时”设置的更多信息'], ]; diff --git a/webapp/packages/plugin-data-import/src/module.ts b/webapp/packages/plugin-data-import/src/module.ts index ef3ca5bf94e..49423eca564 100644 --- a/webapp/packages/plugin-data-import/src/module.ts +++ b/webapp/packages/plugin-data-import/src/module.ts @@ -10,6 +10,7 @@ import { Dependency, ModuleRegistry, proxy, Bootstrap } from '@cloudbeaver/core- import { LocaleService } from './LocaleService.js'; import { DataImportService } from './DataImportService.js'; import { DataImportProcessorsResource } from './DataImportProcessorsResource.js'; +import { DataImportDriverConfigurationResource } from './DataImportDriverConfigurationResource.js'; import { DataImportSettingsService } from './DataImportSettingsService.js'; import { DataImportBootstrap } from './DataImportBootstrap.js'; @@ -25,6 +26,7 @@ export default ModuleRegistry.add({ .addSingleton(Bootstrap, LocaleService) .addSingleton(DataImportService) .addSingleton(DataImportProcessorsResource) + .addSingleton(DataImportDriverConfigurationResource) .addSingleton(DataImportSettingsService); }, }); diff --git a/webapp/packages/plugin-data-import/tsconfig.json b/webapp/packages/plugin-data-import/tsconfig.json index 50882dab413..8e74883116d 100644 --- a/webapp/packages/plugin-data-import/tsconfig.json +++ b/webapp/packages/plugin-data-import/tsconfig.json @@ -16,6 +16,9 @@ { "path": "../core-cli" }, + { + "path": "../core-connections" + }, { "path": "../core-di" }, diff --git a/webapp/yarn.lock b/webapp/yarn.lock index c0159b38add..b40253a4ca2 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -3153,6 +3153,7 @@ __metadata: dependencies: "@cloudbeaver/core-blocks": "workspace:*" "@cloudbeaver/core-cli": "workspace:*" + "@cloudbeaver/core-connections": "workspace:*" "@cloudbeaver/core-di": "workspace:*" "@cloudbeaver/core-dialogs": "workspace:*" "@cloudbeaver/core-events": "workspace:*"