From 655613ccf199ee4ff1ea4e5738839ec32a992ba2 Mon Sep 17 00:00:00 2001 From: Ruslan Musaev <43766501+HocKu7@users.noreply.github.com> Date: Tue, 11 Mar 2025 17:45:24 +0100 Subject: [PATCH] CB-6089 write file stream directly to http response (#3301) * CB-6089 write file stream directly to http response * CB-6089 write file stream directly to http response * CB-6089 write file stream directly to http response * CB-6089 write file stream directly to http response * CB-6089 write file stream directly to http response * CB-6089 remove deprecated method * CB-6089 change ParquetExporter buffer size * CB-6089 remove extra async * CB-6089 add info text --------- Co-authored-by: naumov Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com> Co-authored-by: Ainur <59531286+yagudin10@users.noreply.github.com> --- .../schema/service.data.transfer.graphqls | 2 +- .../data/transfer/DBWServiceDataTransfer.java | 18 ++ .../WebServiceBindingDataTransfer.java | 4 +- .../transfer/impl/WebDataTransferServlet.java | 17 +- .../impl/WebDataTransferSessionConfig.java | 10 +- .../impl/WebDataTransferTaskConfig.java | 43 ++-- .../transfer/impl/WebServiceDataTransfer.java | 216 ++++++++---------- .../data-transfer/removeDataTransferFile.gql | 5 - .../src/DataExportProcessService.ts | 17 +- .../src/DataExportService.ts | 4 - .../ExportNotification.module.css | 5 +- .../ExportNotification/ExportNotification.tsx | 7 +- .../plugin-data-export/src/locales/en.ts | 1 + .../plugin-data-export/src/locales/fr.ts | 1 + .../plugin-data-export/src/locales/it.ts | 1 + .../plugin-data-export/src/locales/ru.ts | 1 + 16 files changed, 152 insertions(+), 200 deletions(-) delete mode 100644 webapp/packages/core-sdk/src/queries/data-transfer/removeDataTransferFile.gql 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 805f1641ea..5a2aeab800 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 @@ -73,6 +73,6 @@ extend type Query { parameters: DataTransferParameters! ): AsyncTaskInfo! - dataTransferRemoveDataFile(dataFileId: String!): Boolean + dataTransferRemoveDataFile(dataFileId: String!): Boolean @deprecated(reason: "25.0.1") } diff --git a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/DBWServiceDataTransfer.java b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/DBWServiceDataTransfer.java index 61b80c8dd5..b1f1a8b97a 100644 --- a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/DBWServiceDataTransfer.java +++ b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/DBWServiceDataTransfer.java @@ -24,11 +24,15 @@ import io.cloudbeaver.service.DBWService; import io.cloudbeaver.service.data.transfer.impl.WebDataTransferDefaultExportSettings; import io.cloudbeaver.service.data.transfer.impl.WebDataTransferParameters; import io.cloudbeaver.service.data.transfer.impl.WebDataTransferStreamProcessor; +import io.cloudbeaver.service.data.transfer.impl.WebDataTransferTaskConfig; import io.cloudbeaver.service.sql.WebSQLContextInfo; import io.cloudbeaver.service.sql.WebSQLProcessor; import io.cloudbeaver.service.sql.WebSQLResultsInfo; import org.jkiss.code.NotNull; +import org.jkiss.dbeaver.DBException; +import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; +import java.io.OutputStream; import java.nio.file.Path; import java.util.List; @@ -62,8 +66,22 @@ public interface DBWServiceDataTransfer extends DBWService { String resultsId, WebDataTransferParameters parameters) throws DBWebException; + /** + * It's deprecated because now we use streaming file to response directly, and we don't need to clean up any files + * after data transfer. + */ @WebAction + @Deprecated Boolean dataTransferRemoveDataFile(WebSession session, String dataFileId) throws DBWebException; WebDataTransferDefaultExportSettings defaultExportSettings(); + + /** + * Usefully for exporting directly to http response and avoid to create temp files. + */ + void exportDataTransferToStream( + @NotNull DBRProgressMonitor monitor, + @NotNull WebDataTransferTaskConfig taskConfig, + @NotNull OutputStream outputStream + ) throws DBException; } diff --git a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebServiceBindingDataTransfer.java b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebServiceBindingDataTransfer.java index 10151d5467..024f52c585 100644 --- a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebServiceBindingDataTransfer.java +++ b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/WebServiceBindingDataTransfer.java @@ -1,6 +1,6 @@ /* * DBeaver - Universal Database Manager - * Copyright (C) 2010-2024 DBeaver Corp and others + * Copyright (C) 2010-2025 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,10 +21,10 @@ import io.cloudbeaver.service.DBWBindingContext; import io.cloudbeaver.service.DBWServiceBindingServlet; import io.cloudbeaver.service.DBWServletContext; import io.cloudbeaver.service.WebServiceBindingBase; +import io.cloudbeaver.service.data.transfer.impl.WebDataTransferImportServlet; import io.cloudbeaver.service.data.transfer.impl.WebDataTransferParameters; import io.cloudbeaver.service.data.transfer.impl.WebDataTransferServlet; import io.cloudbeaver.service.data.transfer.impl.WebServiceDataTransfer; -import io.cloudbeaver.service.data.transfer.impl.WebDataTransferImportServlet; import io.cloudbeaver.service.sql.WebServiceBindingSQL; import org.jkiss.dbeaver.DBException; diff --git a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/impl/WebDataTransferServlet.java b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/impl/WebDataTransferServlet.java index e5af5f39a9..445379e8b3 100644 --- a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/impl/WebDataTransferServlet.java +++ b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/impl/WebDataTransferServlet.java @@ -28,13 +28,8 @@ import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.tools.transfer.registry.DataTransferProcessorDescriptor; import org.jkiss.dbeaver.tools.transfer.registry.DataTransferRegistry; import org.jkiss.utils.CommonUtils; -import org.jkiss.utils.IOUtils; import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Map; public class WebDataTransferServlet extends WebServiceServletBase { @@ -73,20 +68,10 @@ public class WebDataTransferServlet extends WebServiceServletBase { fileName = taskInfo.getDataFileId(); } fileName = WebDataTransferUtils.normalizeFileName(fileName, taskInfo.getParameters().getOutputSettings()); - Path dataFile = taskInfo.getDataFile(); session.addInfoMessage("Download data ..."); response.setHeader("Content-Type", processor.getContentType()); response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\""); - response.setHeader("Content-Length", String.valueOf(Files.size(dataFile))); - try (InputStream is = Files.newInputStream(dataFile)) { - IOUtils.copyStream(is, response.getOutputStream()); - } - - // TODO: cleanup export files ASAP? - if (false) { - dtConfig.removeTask(taskInfo); - } + dtManager.exportDataTransferToStream(session.getProgressMonitor(), taskInfo, response.getOutputStream()); } - } \ No newline at end of file diff --git a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/impl/WebDataTransferSessionConfig.java b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/impl/WebDataTransferSessionConfig.java index 9575c8d517..7196ecd4b1 100644 --- a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/impl/WebDataTransferSessionConfig.java +++ b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/impl/WebDataTransferSessionConfig.java @@ -1,6 +1,6 @@ /* * DBeaver - Universal Database Manager - * Copyright (C) 2010-2024 DBeaver Corp and others + * Copyright (C) 2010-2025 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,16 +36,8 @@ public class WebDataTransferSessionConfig { } } - public void removeTask(WebDataTransferTaskConfig taskConfig) { - synchronized (tasks) { - tasks.remove(taskConfig.getDataFileId()); - taskConfig.deleteFile(); - } - } - public WebDataTransferSessionConfig deleteExportFiles() { synchronized (tasks) { - tasks.values().forEach(WebDataTransferTaskConfig::deleteFile); tasks.clear(); } return this; diff --git a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/impl/WebDataTransferTaskConfig.java b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/impl/WebDataTransferTaskConfig.java index 6a550245c1..d2dd15d269 100644 --- a/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/impl/WebDataTransferTaskConfig.java +++ b/server/bundles/io.cloudbeaver.service.data.transfer/src/io/cloudbeaver/service/data/transfer/impl/WebDataTransferTaskConfig.java @@ -1,6 +1,6 @@ /* * DBeaver - Universal Database Manager - * Copyright (C) 2010-2024 DBeaver Corp and others + * Copyright (C) 2010-2025 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,31 +16,36 @@ */ package io.cloudbeaver.service.data.transfer.impl; +import io.cloudbeaver.service.sql.WebSQLResultsInfo; import org.jkiss.dbeaver.Log; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; +import org.jkiss.dbeaver.model.struct.DBSDataContainer; public class WebDataTransferTaskConfig { private static final Log log = Log.getLog(WebDataTransferTaskConfig.class); - private Path dataFile; + private String fileNameKey; private WebDataTransferParameters parameters; private String exportFileName; + private DBSDataContainer dataContainer; + private WebSQLResultsInfo resultsInfo; - public WebDataTransferTaskConfig(Path dataFile, WebDataTransferParameters parameters) { - this.dataFile = dataFile; + public WebDataTransferTaskConfig( + String fileNameKey, + WebDataTransferParameters parameters, + String exportFileName, + DBSDataContainer dataContainer, + WebSQLResultsInfo webSQLResultsInfo + ) { + this.fileNameKey = fileNameKey; this.parameters = parameters; - } - - public Path getDataFile() { - return dataFile; + this.exportFileName = exportFileName; + this.dataContainer = dataContainer; + this.resultsInfo = webSQLResultsInfo; } public String getDataFileId() { - return dataFile.getFileName().toString(); + return fileNameKey; } public WebDataTransferParameters getParameters() { @@ -51,15 +56,11 @@ public class WebDataTransferTaskConfig { return exportFileName; } - public void setExportFileName(String exportFileName) { - this.exportFileName = exportFileName; + public DBSDataContainer getDataContainer() { + return dataContainer; } - public void deleteFile() { - try { - Files.delete(dataFile); - } catch (IOException e) { - log.error("Error deleting export file " + dataFile.toAbsolutePath(), e); - } + public WebSQLResultsInfo getResultsInfo() { + return resultsInfo; } } \ No newline at end of file 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 2d26f85dfd..310be78fe5 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 @@ -1,6 +1,6 @@ /* * DBeaver - Universal Database Manager - * Copyright (C) 2010-2024 DBeaver Corp and others + * Copyright (C) 2010-2025 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,6 @@ import io.cloudbeaver.DBWebException; import io.cloudbeaver.model.WebAsyncTaskInfo; import io.cloudbeaver.model.session.WebAsyncTaskProcessor; import io.cloudbeaver.model.session.WebSession; -import io.cloudbeaver.server.CBApplication; import io.cloudbeaver.server.CBPlatform; import io.cloudbeaver.service.data.transfer.DBWServiceDataTransfer; import io.cloudbeaver.service.sql.WebSQLContextInfo; @@ -30,14 +29,9 @@ import org.jkiss.code.NotNull; import org.jkiss.code.Nullable; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.Log; -import org.jkiss.dbeaver.model.exec.DBCException; -import org.jkiss.dbeaver.model.exec.DBCResultSet; -import org.jkiss.dbeaver.model.exec.DBCSession; import org.jkiss.dbeaver.model.preferences.DBPPropertyDescriptor; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; import org.jkiss.dbeaver.model.runtime.VoidProgressMonitor; -import org.jkiss.dbeaver.model.sql.DBQuotaException; -import org.jkiss.dbeaver.model.sql.DBSQLException; import org.jkiss.dbeaver.model.struct.DBSDataContainer; import org.jkiss.dbeaver.model.struct.DBSDataManipulator; import org.jkiss.dbeaver.model.struct.DBSEntity; @@ -51,13 +45,11 @@ import org.jkiss.dbeaver.tools.transfer.stream.*; import org.jkiss.dbeaver.utils.ContentUtils; import org.jkiss.utils.CommonUtils; -import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStream; +import java.io.OutputStream; import java.lang.reflect.InvocationTargetException; import java.nio.file.Files; import java.nio.file.Path; -import java.sql.BatchUpdateException; import java.util.*; import java.util.stream.Collectors; @@ -66,8 +58,6 @@ import java.util.stream.Collectors; */ public class WebServiceDataTransfer implements DBWServiceDataTransfer { - public static final String QUOTA_PROP_FILE_LIMIT = "dataExportFileSizeLimit"; - private static final Log log = Log.getLog(WebServiceDataTransfer.class); private final Path dataExportFolder; @@ -145,79 +135,66 @@ public class WebServiceDataTransfer implements DBWServiceDataTransfer { return asyncExportFromDataContainer(sqlContext.getProcessor(), parameters, results.getDataContainer(), results); } - @Override - public Boolean dataTransferRemoveDataFile(WebSession webSession, String dataFileId) throws DBWebException { - WebDataTransferSessionConfig dtConfig = WebDataTransferUtils.getSessionDataTransferConfig(webSession); - WebDataTransferTaskConfig taskInfo = dtConfig.getTask(dataFileId); - if (taskInfo == null) { - throw new DBWebException("Session task '" + dataFileId + "' not found"); - } - Path dataFile = taskInfo.getDataFile(); - if (dataFile != null) { - try { - Files.delete(dataFile); - } catch (IOException e) { - log.warn("Error deleting data file '" + dataFile.toAbsolutePath() + "'", e); - } - } - dtConfig.removeTask(taskInfo); - - return true; - } - @Override public WebDataTransferDefaultExportSettings defaultExportSettings() { return new WebDataTransferDefaultExportSettings(); } - private WebAsyncTaskInfo asyncExportFromDataContainer(WebSQLProcessor sqlProcessor, WebDataTransferParameters parameters, DBSDataContainer dataContainer, - @Nullable WebSQLResultsInfo resultsInfo) { + @Override + @Deprecated + public Boolean dataTransferRemoveDataFile(WebSession webSession, String dataFileId) throws DBWebException { + //deprecated + return true; + } + + @Override + public void exportDataTransferToStream( + @NotNull DBRProgressMonitor monitor, + @NotNull WebDataTransferTaskConfig taskConfig, + @NotNull OutputStream outputStream + ) throws DBException { + + WebDataTransferParameters parameters = taskConfig.getParameters(); + DBSDataContainer dataContainer = taskConfig.getDataContainer(); + WebSQLResultsInfo resultsInfo = taskConfig.getResultsInfo(); + DataTransferProcessorDescriptor processor = DataTransferRegistry.getInstance().getProcessor(parameters.getProcessorId()); + + try { + exportData(monitor, processor, dataContainer, parameters, resultsInfo, outputStream); + } catch (Exception e) { + throw new DBException("Error exporting data", e); + } + } + + private WebAsyncTaskInfo asyncExportFromDataContainer( + @NotNull WebSQLProcessor sqlProcessor, + @NotNull WebDataTransferParameters parameters, + @NotNull DBSDataContainer dataContainer, + @Nullable WebSQLResultsInfo resultsInfo + ) { sqlProcessor.getWebSession().addInfoMessage("Export data"); DataTransferProcessorDescriptor processor = DataTransferRegistry.getInstance().getProcessor(parameters.getProcessorId()); - WebAsyncTaskProcessor runnable = new WebAsyncTaskProcessor() { - @Override - public void run(DBRProgressMonitor monitor) throws InvocationTargetException { - monitor.beginTask("Export data", 1); - try { - monitor.subTask("Export data using " + processor.getName()); - Path exportFile = dataExportFolder.resolve( - makeUniqueFileName(sqlProcessor, processor, parameters.getProcessorProperties())); - try { - exportData(monitor, processor, dataContainer, parameters, resultsInfo, exportFile); - } catch (Exception e) { - if (Files.exists(exportFile)) { - try { - Files.delete(exportFile); - } catch (IOException ex) { - log.error("Error deleting export file " + exportFile.toAbsolutePath(), e); - } - } - if (e instanceof DBException) { - throw e; - } - throw new DBException("Error exporting data", e); - } - var outputSettings = parameters.getOutputSettings(); - Path finallyExportFile = outputSettings.isCompress() - ? exportFile.resolveSibling(WebDataTransferUtils.normalizeFileName( - exportFile.getFileName().toString(), outputSettings)) - : exportFile; - WebDataTransferTaskConfig taskConfig = new WebDataTransferTaskConfig(finallyExportFile, parameters); - String exportFileName = CommonUtils.isEmpty(outputSettings.getFileName()) ? - CommonUtils.escapeFileName(CommonUtils.truncateString(dataContainer.getName(), 32)) : - outputSettings.getFileName(); - taskConfig.setExportFileName(exportFileName); - WebDataTransferUtils.getSessionDataTransferConfig(sqlProcessor.getWebSession()).addTask(taskConfig); + String uniqueFileName = makeUniqueFileName(sqlProcessor, processor, parameters.getProcessorProperties()); + var outputSettings = parameters.getOutputSettings(); + String fileNameKey = WebDataTransferUtils.normalizeFileName(uniqueFileName, outputSettings); + String exportFileName = CommonUtils.isEmpty(outputSettings.getFileName()) + ? CommonUtils.escapeFileName(CommonUtils.truncateString(dataContainer.getName(), 32)) + : outputSettings.getFileName(); + WebDataTransferTaskConfig taskConfig = new WebDataTransferTaskConfig( + fileNameKey, parameters, exportFileName, dataContainer, resultsInfo); - result = finallyExportFile.getFileName().toString(); - } catch (Throwable e) { - throw new InvocationTargetException(e); - } finally { - monitor.done(); + WebDataTransferUtils.getSessionDataTransferConfig(sqlProcessor.getWebSession()) + .addTask(taskConfig); + + //fixme fake task for keeping api + return sqlProcessor.getWebSession().createAndRunAsyncTask( + "Data export", new WebAsyncTaskProcessor<>() { + @Override + public void run(DBRProgressMonitor monitor) throws InvocationTargetException { + result = fileNameKey; } } - }; - return sqlProcessor.getWebSession().createAndRunAsyncTask("Data export", runnable); + ); } public WebAsyncTaskInfo asyncImportDataContainer(@NotNull String processorId, @@ -263,34 +240,50 @@ public class WebServiceDataTransfer implements DBWServiceDataTransfer { DBSDataContainer dataContainer, WebDataTransferParameters parameters, WebSQLResultsInfo resultsInfo, - Path exportFile) throws DBException, IOException - { + OutputStream outputStream + ) throws DBException, IOException { IDataTransferProcessor processorInstance = processor.getInstance(); - if (!(processorInstance instanceof IStreamDataExporter)) { + if (!(processorInstance instanceof IStreamDataExporter exporter)) { throw new DBException("Invalid processor. " + IStreamDataExporter.class.getSimpleName() + " expected"); } - IStreamDataExporter exporter = (IStreamDataExporter) processorInstance; - Number fileSizeLimit = CBApplication.getInstance().getAppConfiguration().getResourceQuota(QUOTA_PROP_FILE_LIMIT); + Map processorProperties = parameters.getProcessorProperties(); + if (processorProperties == null) processorProperties = Collections.emptyMap(); + Map properties = new HashMap<>(); + for (DBPPropertyDescriptor prop : processor.getProperties()) { + Object propValue = processorProperties.get(CommonUtils.toString(prop.getId())); + properties.put(prop.getId(), propValue != null ? propValue : prop.getDefaultValue()); + } + // Remove extension property (we specify file name directly) + properties.remove(StreamConsumerSettings.PROP_FILE_EXTENSION); - StreamTransferConsumer consumer = new StreamTransferConsumer() { - @Override - public void fetchRow(@NotNull DBCSession session, @NotNull DBCResultSet resultSet) throws DBCException { - super.fetchRow(session, resultSet); - if (fileSizeLimit != null && getBytesWritten() > fileSizeLimit.longValue()) { - throw new DBQuotaException( - "Data export quota exceeded \n Please increase the resourceQuotas parameter in configuration", - QUOTA_PROP_FILE_LIMIT, fileSizeLimit.longValue(), getBytesWritten() - ); - } - } - }; + DatabaseProducerSettings producerSettings = new DatabaseProducerSettings(); + producerSettings.setExtractType(DatabaseProducerSettings.ExtractType.SINGLE_QUERY); + producerSettings.setQueryRowCount(false); + producerSettings.setOpenNewConnections(CommonUtils.getOption(parameters.getDbProducerSettings(), "openNewConnection")); + StreamTransferConsumer consumer = new StreamTransferConsumer(); + StreamConsumerSettings settings = makeStreamConsumerSettings(parameters); + DatabaseTransferProducer producer = new DatabaseTransferProducer( + dataContainer, + parameters.getFilter() == null ? null : parameters.getFilter().makeDataFilter(resultsInfo)); + consumer.initTransfer( + dataContainer, + settings, + new IDataTransferConsumer.TransferParameters(processor.isBinaryFormat(), processor.isHTMLFormat(), outputStream), + exporter, + properties, + producer.getProject()); + + producer.transferData(monitor, consumer, null, producerSettings, null); + + consumer.finishTransfer(monitor, false); + } + + @NotNull + private StreamConsumerSettings makeStreamConsumerSettings(@NotNull WebDataTransferParameters parameters) { StreamConsumerSettings settings = new StreamConsumerSettings(); - settings.setOutputFolder(exportFile.getParent().toAbsolutePath().toString()); - settings.setOutputFilePattern(exportFile.getFileName().toString()); - WebDataTransferOutputSettings outputSettings = parameters.getOutputSettings(); settings.setOutputEncodingBOM(outputSettings.isInsertBom()); settings.setCompressResults(outputSettings.isCompress()); @@ -300,38 +293,7 @@ public class WebServiceDataTransfer implements DBWServiceDataTransfer { if (!CommonUtils.isEmpty(outputSettings.getTimestampPattern())) { settings.setOutputTimestampPattern(outputSettings.getTimestampPattern()); } - - Map properties = new HashMap<>(); - - Map processorProperties = parameters.getProcessorProperties(); - if (processorProperties == null) processorProperties = Collections.emptyMap(); - for (DBPPropertyDescriptor prop : processor.getProperties()) { - Object propValue = processorProperties.get(CommonUtils.toString(prop.getId())); - properties.put(prop.getId(), propValue != null ? propValue : prop.getDefaultValue()); - } - // Remove extension property (we specify file name directly) - properties.remove(StreamConsumerSettings.PROP_FILE_EXTENSION); - - - DatabaseTransferProducer producer = new DatabaseTransferProducer( - dataContainer, - parameters.getFilter() == null ? null : parameters.getFilter().makeDataFilter(resultsInfo)); - DatabaseProducerSettings producerSettings = new DatabaseProducerSettings(); - producerSettings.setExtractType(DatabaseProducerSettings.ExtractType.SINGLE_QUERY); - producerSettings.setQueryRowCount(false); - producerSettings.setOpenNewConnections(CommonUtils.getOption(parameters.getDbProducerSettings(), "openNewConnection")); - - consumer.initTransfer( - dataContainer, - settings, - new IDataTransferConsumer.TransferParameters(processor.isBinaryFormat(), processor.isHTMLFormat()), - exporter, - properties, - producer.getProject()); - - producer.transferData(monitor, consumer, null, producerSettings, null); - - consumer.finishTransfer(monitor, false); + return settings; } private void importData( diff --git a/webapp/packages/core-sdk/src/queries/data-transfer/removeDataTransferFile.gql b/webapp/packages/core-sdk/src/queries/data-transfer/removeDataTransferFile.gql deleted file mode 100644 index 601dfd983a..0000000000 --- a/webapp/packages/core-sdk/src/queries/data-transfer/removeDataTransferFile.gql +++ /dev/null @@ -1,5 +0,0 @@ -query removeDataTransferFile( - $dataFileId: String! -) { - result: dataTransferRemoveDataFile(dataFileId: $dataFileId) -} diff --git a/webapp/packages/plugin-data-export/src/DataExportProcessService.ts b/webapp/packages/plugin-data-export/src/DataExportProcessService.ts index b32df380b8..902a8e1707 100644 --- a/webapp/packages/plugin-data-export/src/DataExportProcessService.ts +++ b/webapp/packages/plugin-data-export/src/DataExportProcessService.ts @@ -36,7 +36,7 @@ export class DataExportProcessService { private readonly notificationService: NotificationService, ) {} - async cancel(exportId: string): Promise { + cancel(exportId: string): void { const process = this.exportProcesses.get(exportId); if (!process) { return; @@ -44,21 +44,14 @@ export class DataExportProcessService { process.process.cancel(); } - async delete(exportId: string): Promise { + delete(exportId: string): void { const process = this.exportProcesses.get(exportId); + if (!process) { return; } - try { - const dataFileId = process.process.getPayload(); - if (dataFileId) { - await this.graphQLService.sdk.removeDataTransferFile({ dataFileId }); - } - } catch (exception: any) { - this.notificationService.logException(exception, 'Error occurred while deleting file'); - } finally { - this.exportProcesses.remove(exportId); - } + + this.exportProcesses.remove(exportId); } download(exportId: string): void { diff --git a/webapp/packages/plugin-data-export/src/DataExportService.ts b/webapp/packages/plugin-data-export/src/DataExportService.ts index f0d9d2ea83..23201ac1af 100644 --- a/webapp/packages/plugin-data-export/src/DataExportService.ts +++ b/webapp/packages/plugin-data-export/src/DataExportService.ts @@ -24,10 +24,6 @@ export class DataExportService { await this.dataExportProcessService.cancel(exportId); } - async delete(exportId: string): Promise { - await this.dataExportProcessService.delete(exportId); - } - download(exportId: string): void { this.dataExportProcessService.download(exportId); } diff --git a/webapp/packages/plugin-data-export/src/ExportNotification/ExportNotification.module.css b/webapp/packages/plugin-data-export/src/ExportNotification/ExportNotification.module.css index dbbc2d4099..037c7e1511 100644 --- a/webapp/packages/plugin-data-export/src/ExportNotification/ExportNotification.module.css +++ b/webapp/packages/plugin-data-export/src/ExportNotification/ExportNotification.module.css @@ -14,8 +14,11 @@ white-space: pre-line; } -.sourceName { +.subText { composes: theme-typography--body2 from global; +} + +.sourceName { padding-top: 16px; max-height: 50px; overflow: hidden; diff --git a/webapp/packages/plugin-data-export/src/ExportNotification/ExportNotification.tsx b/webapp/packages/plugin-data-export/src/ExportNotification/ExportNotification.tsx index 4cb1da02a7..c3cb1cfc4b 100644 --- a/webapp/packages/plugin-data-export/src/ExportNotification/ExportNotification.tsx +++ b/webapp/packages/plugin-data-export/src/ExportNotification/ExportNotification.tsx @@ -15,6 +15,7 @@ import { SnackbarFooter, SnackbarStatus, SnackbarWrapper, + Text, useErrorDetails, useS, useTranslate, @@ -37,14 +38,16 @@ export const ExportNotification = observer(function ExportNotification({ const errorDetails = useErrorDetails(state.task?.process.getRejectionReason() ?? null); const { title, status, message } = state.status; + const isReadyToDownload = status === ENotificationType.Info && !!state.downloadUrl; return ( + {isReadyToDownload && {translate('plugin_data_export_download_process_info')}} {message &&
{message}
} -
+
{state.sourceName} {state.task?.context.query && (
@@ -54,7 +57,7 @@ export const ExportNotification = observer(function ExportNotification({
           
- {status === ENotificationType.Info && state.downloadUrl && ( + {isReadyToDownload && ( <>