From 54ec5ea2dcbb1a585c518ab5ede90e28d3586aa9 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Tue, 15 Mar 2022 12:03:16 +0000 Subject: [PATCH 1/3] fix button action export for csv --- packages/client/src/utils/buttonActions.js | 27 +++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/packages/client/src/utils/buttonActions.js b/packages/client/src/utils/buttonActions.js index 25659cea98..17121f3ee6 100644 --- a/packages/client/src/utils/buttonActions.js +++ b/packages/client/src/utils/buttonActions.js @@ -241,6 +241,24 @@ const s3UploadHandler = async action => { } } +const convertToCsv = (headers, rows) => { + let csv = headers.map(key => `"${key}"`).join(",") + + for (let row of rows) { + csv = `${csv}\n${headers + .map(header => { + let val = row[header] + val = + typeof val === "object" + ? `"${JSON.stringify(val).replace(/"/g, "'")}"` + : `"${val}"` + return val.trim() + }) + .join(",")}` + } + return csv +} + const exportDataHandler = async action => { let selection = rowSelectionStore.actions.getSelection( action.parameters.tableComponentId @@ -252,7 +270,14 @@ const exportDataHandler = async action => { rows: selection.selectedRows, }) - download(JSON.stringify(data), `export.${action.parameters.type}`) + let dataToExport + const headers = Object.keys(data[0]) + if (action.parameters.type === "csv") { + dataToExport = convertToCsv(headers, data) + } else { + dataToExport = JSON.stringify(data) + } + download(dataToExport, `export.${action.parameters.type}`) } catch (error) { notificationStore.actions.error("There was an error exporting the data") } From a2c32575158b7265ed6f54379e31c849decb0a07 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Wed, 16 Mar 2022 10:22:06 +0000 Subject: [PATCH 2/3] send export file from backend --- packages/client/src/utils/buttonActions.js | 28 ++----------------- packages/frontend-core/src/api/rows.js | 7 +++-- .../src/api/controllers/row/external.js | 16 +++++++++-- .../src/api/controllers/row/internal.js | 13 +++++++-- 4 files changed, 32 insertions(+), 32 deletions(-) diff --git a/packages/client/src/utils/buttonActions.js b/packages/client/src/utils/buttonActions.js index 17121f3ee6..594bee6953 100644 --- a/packages/client/src/utils/buttonActions.js +++ b/packages/client/src/utils/buttonActions.js @@ -241,23 +241,6 @@ const s3UploadHandler = async action => { } } -const convertToCsv = (headers, rows) => { - let csv = headers.map(key => `"${key}"`).join(",") - - for (let row of rows) { - csv = `${csv}\n${headers - .map(header => { - let val = row[header] - val = - typeof val === "object" - ? `"${JSON.stringify(val).replace(/"/g, "'")}"` - : `"${val}"` - return val.trim() - }) - .join(",")}` - } - return csv -} const exportDataHandler = async action => { let selection = rowSelectionStore.actions.getSelection( @@ -268,16 +251,9 @@ const exportDataHandler = async action => { const data = await API.exportRows({ tableId: selection.tableId, rows: selection.selectedRows, + format: action.parameters.type, }) - - let dataToExport - const headers = Object.keys(data[0]) - if (action.parameters.type === "csv") { - dataToExport = convertToCsv(headers, data) - } else { - dataToExport = JSON.stringify(data) - } - download(dataToExport, `export.${action.parameters.type}`) + download(data, `${selection.tableId}.${action.parameters.type}`) } catch (error) { notificationStore.actions.error("There was an error exporting the data") } diff --git a/packages/frontend-core/src/api/rows.js b/packages/frontend-core/src/api/rows.js index 1b6efe624f..6a0d278cf7 100644 --- a/packages/frontend-core/src/api/rows.js +++ b/packages/frontend-core/src/api/rows.js @@ -66,12 +66,15 @@ export const buildRowEndpoints = API => ({ * @param tableId the table ID to export the rows from * @param rows the array of rows to export */ - exportRows: async ({ tableId, rows }) => { + exportRows: async ({ tableId, rows, format }) => { return await API.post({ - url: `/api/${tableId}/rows/exportRows`, + url: `/api/${tableId}/rows/exportRows?format=${format}`, body: { rows, }, + parseResponse: async response => { + return await response.text() + }, }) }, }) diff --git a/packages/server/src/api/controllers/row/external.js b/packages/server/src/api/controllers/row/external.js index aebcfce724..2f816e11a9 100644 --- a/packages/server/src/api/controllers/row/external.js +++ b/packages/server/src/api/controllers/row/external.js @@ -10,6 +10,8 @@ const { } = require("../../../integrations/utils") const ExternalRequest = require("./ExternalRequest") const { getAppDB } = require("@budibase/backend-core/context") +const exporters = require("../view/exporters") +const { apiFileReturn } = require("../../../utilities/fileSystem") async function handleRequest(operation, tableId, opts = {}) { // make sure the filters are cleaned up, no empty strings for equals, fuzzy or string @@ -155,6 +157,7 @@ exports.validate = async () => { exports.exportRows = async ctx => { const { datasourceId, tableName } = breakExternalTableId(ctx.params.tableId) const db = getAppDB() + let format = ctx.query.format const datasource = await db.get(datasourceId) if (!datasource || !datasource.entities) { ctx.throw(400, "Datasource has not been configured for plus API.") @@ -164,13 +167,22 @@ exports.exportRows = async ctx => { ctx.request.body = { query: { oneOf: { - [table.primaryDisplay]: ctx.request.body.map( + [table.primaryDisplay]: ctx.request.body.rows.map( id => breakRowIdField(id)[0] ), }, }, } - return exports.search(ctx) + + let result = await exports.search(ctx) + + let headers = Object.keys(result.rows[0]) + const exporter = exporters[format] + const filename = `export.${format}` + + // send down the file + ctx.attachment(filename) + return apiFileReturn(exporter(headers, result.rows)) } exports.fetchEnrichedRow = async ctx => { diff --git a/packages/server/src/api/controllers/row/internal.js b/packages/server/src/api/controllers/row/internal.js index 068a485a8a..4b57e09029 100644 --- a/packages/server/src/api/controllers/row/internal.js +++ b/packages/server/src/api/controllers/row/internal.js @@ -27,6 +27,8 @@ const { const { cloneDeep } = require("lodash/fp") const { getAppDB } = require("@budibase/backend-core/context") const { finaliseRow, updateRelatedFormula } = require("./staticFormula") +const exporters = require("../view/exporters") +const { apiFileReturn } = require("../../../utilities/fileSystem") const CALCULATION_TYPES = { SUM: "sum", @@ -366,6 +368,7 @@ exports.exportRows = async ctx => { const db = getAppDB() const table = await db.get(ctx.params.tableId) const rowIds = ctx.request.body.rows + let format = ctx.query.format let response = ( await db.allDocs({ include_docs: true, @@ -374,8 +377,14 @@ exports.exportRows = async ctx => { ).rows.map(row => row.doc) let rows = await outputProcessing(table, response) - - return rows + + let headers = Object.keys(rows[0]) + const exporter = exporters[format] + const filename = `export.${format}` + + // send down the file + ctx.attachment(filename) + return apiFileReturn(exporter(headers, rows)) } exports.fetchEnrichedRow = async ctx => { From fcfce77d5d331842515de46505b8cdfd7aad9fc0 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Wed, 16 Mar 2022 10:33:38 +0000 Subject: [PATCH 3/3] linting --- packages/client/src/utils/buttonActions.js | 1 - packages/server/src/api/controllers/row/external.js | 2 +- packages/server/src/api/controllers/row/internal.js | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/client/src/utils/buttonActions.js b/packages/client/src/utils/buttonActions.js index 594bee6953..f44e7d7453 100644 --- a/packages/client/src/utils/buttonActions.js +++ b/packages/client/src/utils/buttonActions.js @@ -241,7 +241,6 @@ const s3UploadHandler = async action => { } } - const exportDataHandler = async action => { let selection = rowSelectionStore.actions.getSelection( action.parameters.tableComponentId diff --git a/packages/server/src/api/controllers/row/external.js b/packages/server/src/api/controllers/row/external.js index 2f816e11a9..a8c8c0a627 100644 --- a/packages/server/src/api/controllers/row/external.js +++ b/packages/server/src/api/controllers/row/external.js @@ -179,7 +179,7 @@ exports.exportRows = async ctx => { let headers = Object.keys(result.rows[0]) const exporter = exporters[format] const filename = `export.${format}` - + // send down the file ctx.attachment(filename) return apiFileReturn(exporter(headers, result.rows)) diff --git a/packages/server/src/api/controllers/row/internal.js b/packages/server/src/api/controllers/row/internal.js index 4b57e09029..8801827649 100644 --- a/packages/server/src/api/controllers/row/internal.js +++ b/packages/server/src/api/controllers/row/internal.js @@ -377,11 +377,11 @@ exports.exportRows = async ctx => { ).rows.map(row => row.doc) let rows = await outputProcessing(table, response) - + let headers = Object.keys(rows[0]) const exporter = exporters[format] const filename = `export.${format}` - + // send down the file ctx.attachment(filename) return apiFileReturn(exporter(headers, rows))