From 5b5e7e47a2ceab5a37fb3e491d2b3dce26deb5a3 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 13 May 2024 18:11:40 +0100 Subject: [PATCH 1/2] Fixing some issues with updating rows with attachments, there were some UI bugs that had been raised (undefined being displayed) and the uploading from a URL was not working as expected, due to the stream not matching the expected format of the S3 SDK - this has been fixed by converting the stream to a true readable stream with . --- .../backend-core/src/objectStore/utils.ts | 9 ++--- .../SetupPanel/AutomationBlockSetup.svelte | 19 +++++----- .../SetupPanel/RowSelectorTypes.svelte | 12 +++---- .../server/src/automations/automationUtils.ts | 4 +-- .../server/src/automations/steps/updateRow.ts | 23 ++++++------ .../src/utilities/rowProcessor/attachments.ts | 21 ++++++++--- .../src/utilities/rowProcessor/index.ts | 36 ++++++++++--------- 7 files changed, 70 insertions(+), 54 deletions(-) diff --git a/packages/backend-core/src/objectStore/utils.ts b/packages/backend-core/src/objectStore/utils.ts index 5b9c2e3646..2edb075b92 100644 --- a/packages/backend-core/src/objectStore/utils.ts +++ b/packages/backend-core/src/objectStore/utils.ts @@ -9,6 +9,9 @@ import { AutomationAttachmentContent, BucketedContent, } from "@budibase/types" +import stream from "stream" +import streamWeb from "node:stream/web" + /**************************************************** * NOTE: When adding a new bucket - name * * sure that S3 usages (like budibase-infra) * @@ -53,12 +56,10 @@ export const bucketTTLConfig = ( Rules: [lifecycleRule], } - const params = { + return { Bucket: bucketName, LifecycleConfiguration: lifecycleConfiguration, } - - return params } async function processUrlAttachment( @@ -71,7 +72,7 @@ async function processUrlAttachment( const fallbackFilename = path.basename(new URL(attachment.url).pathname) return { filename: attachment.filename || fallbackFilename, - content: response.body, + content: stream.Readable.fromWeb(response.body as streamWeb.ReadableStream), } } diff --git a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte index 85af5bbafd..879927343f 100644 --- a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte +++ b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte @@ -374,6 +374,16 @@ return `${value.title || (key === "row" ? "Table" : key)} ${requiredSuffix}` } + function handleAttachmentParams(keyValueObj) { + let params = {} + if (keyValueObj?.length) { + for (let param of keyValueObj) { + params[param.url] = param.filename + } + } + return params + } + onMount(async () => { try { await environment.loadVariables() @@ -381,15 +391,6 @@ console.error(error) } }) - const handleAttachmentParams = keyValuObj => { - let params = {} - if (keyValuObj?.length) { - for (let param of keyValuObj) { - params[param.url] = param.filename - } - } - return params - }
diff --git a/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte b/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte index 9b4e5e36f6..1b52e35314 100644 --- a/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte +++ b/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte @@ -25,21 +25,21 @@ return !!schema.constraints?.inclusion?.length } - const handleAttachmentParams = keyValuObj => { + function handleAttachmentParams(keyValueObj) { let params = {} if ( schema.type === FieldType.ATTACHMENT_SINGLE && - Object.keys(keyValuObj).length === 0 + Object.keys(keyValueObj).length === 0 ) { return [] } - if (!Array.isArray(keyValuObj)) { - keyValuObj = [keyValuObj] + if (!Array.isArray(keyValueObj) && keyValueObj) { + keyValueObj = [keyValueObj] } - if (keyValuObj.length) { - for (let param of keyValuObj) { + if (keyValueObj.length) { + for (let param of keyValueObj) { params[param.url] = param.filename } } diff --git a/packages/server/src/automations/automationUtils.ts b/packages/server/src/automations/automationUtils.ts index c94c166be1..88b279b771 100644 --- a/packages/server/src/automations/automationUtils.ts +++ b/packages/server/src/automations/automationUtils.ts @@ -163,7 +163,7 @@ async function generateAttachmentRow(attachment: AutomationAttachment) { try { const { filename } = attachment - const extension = path.extname(filename) + const extension = path.extname(filename).replaceAll(".", "") const attachmentResult = await objectStore.processAutomationAttachment( attachment ) @@ -183,7 +183,7 @@ async function generateAttachmentRow(attachment: AutomationAttachment) { return { size, name: filename, - extension, + extension: extension, key: s3Key, } } catch (error) { diff --git a/packages/server/src/automations/steps/updateRow.ts b/packages/server/src/automations/steps/updateRow.ts index 348c5e8373..32c8addd7a 100644 --- a/packages/server/src/automations/steps/updateRow.ts +++ b/packages/server/src/automations/steps/updateRow.ts @@ -94,18 +94,6 @@ export async function run({ inputs, appId, emitter }: AutomationStepInput) { } } - // have to clean up the row, remove the table from it - const ctx: any = buildCtx(appId, emitter, { - body: { - ...inputs.row, - _id: inputs.rowId, - }, - params: { - rowId: inputs.rowId, - tableId: tableId, - }, - }) - try { if (tableId) { inputs.row = await automationUtils.cleanUpRow( @@ -118,6 +106,17 @@ export async function run({ inputs, appId, emitter }: AutomationStepInput) { inputs.row ) } + // have to clean up the row, remove the table from it + const ctx: any = buildCtx(appId, emitter, { + body: { + ...inputs.row, + _id: inputs.rowId, + }, + params: { + rowId: inputs.rowId, + tableId: tableId, + }, + }) await rowController.patch(ctx) return { row: ctx.body, diff --git a/packages/server/src/utilities/rowProcessor/attachments.ts b/packages/server/src/utilities/rowProcessor/attachments.ts index da52d6a631..bfa216c25b 100644 --- a/packages/server/src/utilities/rowProcessor/attachments.ts +++ b/packages/server/src/utilities/rowProcessor/attachments.ts @@ -1,6 +1,12 @@ import { ObjectStoreBuckets } from "../../constants" import { context, db as dbCore, objectStore } from "@budibase/backend-core" -import { FieldType, RenameColumn, Row, Table } from "@budibase/types" +import { + FieldType, + RenameColumn, + Row, + RowAttachment, + Table, +} from "@budibase/types" export class AttachmentCleanup { static async coreCleanup(fileListFn: () => string[]): Promise { @@ -21,7 +27,7 @@ export class AttachmentCleanup { private static extractAttachmentKeys( type: FieldType, - rowData: any + rowData: RowAttachment[] | RowAttachment ): string[] { if ( type !== FieldType.ATTACHMENTS && @@ -34,10 +40,15 @@ export class AttachmentCleanup { return [] } - if (type === FieldType.ATTACHMENTS) { - return rowData.map((attachment: any) => attachment.key) + if (type === FieldType.ATTACHMENTS && Array.isArray(rowData)) { + return rowData + .filter(attachment => attachment.key) + .map(attachment => attachment.key) + } else if ("key" in rowData) { + return [rowData.key] } - return [rowData.key] + + return [] } private static async tableChange( diff --git a/packages/server/src/utilities/rowProcessor/index.ts b/packages/server/src/utilities/rowProcessor/index.ts index b71f40e870..e7bc725285 100644 --- a/packages/server/src/utilities/rowProcessor/index.ts +++ b/packages/server/src/utilities/rowProcessor/index.ts @@ -1,11 +1,11 @@ import * as linkRows from "../../db/linkedRows" -import { processFormulas, fixAutoColumnSubType } from "./utils" +import { fixAutoColumnSubType, processFormulas } from "./utils" import { objectStore, utils } from "@budibase/backend-core" import { InternalTables } from "../../db/utils" import { TYPE_TRANSFORM_MAP } from "./map" import { - FieldType, AutoFieldSubType, + FieldType, Row, RowAttachment, Table, @@ -221,27 +221,31 @@ export async function outputProcessing( opts.squash = true } - // process complex types: attachements, bb references... + // process complex types: attachments, bb references... for (let [property, column] of Object.entries(table.schema)) { - if (column.type === FieldType.ATTACHMENTS) { + if ( + column.type === FieldType.ATTACHMENTS || + column.type === FieldType.ATTACHMENT_SINGLE + ) { for (let row of enriched) { - if (row[property] == null || !Array.isArray(row[property])) { + if (row[property] == null) { continue } - row[property].forEach((attachment: RowAttachment) => { - if (!attachment.url) { + const process = (attachment: RowAttachment) => { + if (!attachment.url && attachment.key) { attachment.url = objectStore.getAppFileUrl(attachment.key) } - }) - } - } else if (column.type === FieldType.ATTACHMENT_SINGLE) { - for (let row of enriched) { - if (!row[property] || Object.keys(row[property]).length === 0) { - continue + return attachment } - - if (!row[property].url) { - row[property].url = objectStore.getAppFileUrl(row[property].key) + if (typeof row[property] === "string") { + row[property] = JSON.parse(row[property]) + } + if (Array.isArray(row[property])) { + row[property].forEach((attachment: RowAttachment) => { + process(attachment) + }) + } else { + process(row[property]) } } } else if ( From 39147b80b627e0d26359f7aa176ed5fe3354e6ab Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 14 May 2024 11:23:50 +0100 Subject: [PATCH 2/2] PR comments. --- packages/backend-core/src/objectStore/utils.ts | 3 +++ packages/server/src/automations/automationUtils.ts | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/backend-core/src/objectStore/utils.ts b/packages/backend-core/src/objectStore/utils.ts index 2edb075b92..30c2fefbf1 100644 --- a/packages/backend-core/src/objectStore/utils.ts +++ b/packages/backend-core/src/objectStore/utils.ts @@ -70,6 +70,9 @@ async function processUrlAttachment( throw new Error(`Unexpected response ${response.statusText}`) } const fallbackFilename = path.basename(new URL(attachment.url).pathname) + if (!response.body) { + throw new Error("No response received for attachment") + } return { filename: attachment.filename || fallbackFilename, content: stream.Readable.fromWeb(response.body as streamWeb.ReadableStream), diff --git a/packages/server/src/automations/automationUtils.ts b/packages/server/src/automations/automationUtils.ts index 88b279b771..cb09f860da 100644 --- a/packages/server/src/automations/automationUtils.ts +++ b/packages/server/src/automations/automationUtils.ts @@ -163,7 +163,10 @@ async function generateAttachmentRow(attachment: AutomationAttachment) { try { const { filename } = attachment - const extension = path.extname(filename).replaceAll(".", "") + let extension = path.extname(filename) + if (extension.startsWith(".")) { + extension = extension.substring(1, extension.length) + } const attachmentResult = await objectStore.processAutomationAttachment( attachment ) @@ -182,8 +185,8 @@ async function generateAttachmentRow(attachment: AutomationAttachment) { return { size, + extension, name: filename, - extension: extension, key: s3Key, } } catch (error) {