diff --git a/components/google_sheets/actions/add-multiple-rows/add-multiple-rows.mjs b/components/google_sheets/actions/add-multiple-rows/add-multiple-rows.mjs index b3f8871878c2b..ab1e520b82ff8 100644 --- a/components/google_sheets/actions/add-multiple-rows/add-multiple-rows.mjs +++ b/components/google_sheets/actions/add-multiple-rows/add-multiple-rows.mjs @@ -11,7 +11,7 @@ export default { key: "google_sheets-add-multiple-rows", name: "Add Multiple Rows", description: "Add multiple rows of data to a Google Sheet. [See the documentation](https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/append)", - version: "0.2.8", + version: "0.2.9", type: "action", props: { googleSheets, diff --git a/components/google_sheets/actions/add-single-row/add-single-row.mjs b/components/google_sheets/actions/add-single-row/add-single-row.mjs index 5fdf7eee92953..9958fda2c7437 100644 --- a/components/google_sheets/actions/add-single-row/add-single-row.mjs +++ b/components/google_sheets/actions/add-single-row/add-single-row.mjs @@ -1,6 +1,7 @@ import common from "../common/worksheet.mjs"; import { ConfigurationError } from "@pipedream/platform"; import { parseArray } from "../../common/utils.mjs"; +import { isDynamicExpression } from "../common/worksheet.mjs"; const { googleSheets } = common.props; @@ -9,7 +10,7 @@ export default { key: "google_sheets-add-single-row", name: "Add Single Row", description: "Add a single row of data to Google Sheets. [See the documentation](https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/append)", - version: "2.1.10", + version: "2.1.11", type: "action", props: { googleSheets, @@ -27,6 +28,7 @@ export default { driveId: googleSheets.methods.getDriveId(c.drive), }), ], + reloadProps: true, }, worksheetId: { propDefinition: [ @@ -36,42 +38,81 @@ export default { sheetId: c.sheetId?.value || c.sheetId, }), ], - type: "string", - label: "Worksheet ID", - }, - hasHeaders: { - type: "boolean", - label: "Does the first row of the sheet have headers?", - description: "If the first row of your document has headers, we'll retrieve them to make it easy to enter the value for each column. Please note, that if you are referencing a worksheet using a custom expression referencing data from another step, e.g. `{{steps.my_step.$return_value}}` this prop cannot be used. If you want to retrieve the header row, select both **Spreadsheet** and **Worksheet ID** from the dropdowns above.", + description: "Select a worksheet or enter a custom expression. When referencing a spreadsheet dynamically, you must provide a custom expression for the worksheet.", + async options({ sheetId }) { + // If sheetId is a dynamic reference, don't load options + if (isDynamicExpression(sheetId)) { + return []; + } + + // Otherwise, call the original options function with the correct context + const origOptions = googleSheets.propDefinitions.worksheetIDs.options; + return origOptions.call(this, { + sheetId, + }); + }, reloadProps: true, }, + hasHeaders: common.props.hasHeaders, }, async additionalProps() { const { sheetId, worksheetId, + hasHeaders, } = this; + // If using dynamic expressions for either sheetId or worksheetId, return only array input + if (isDynamicExpression(sheetId) || isDynamicExpression(worksheetId)) { + return { + myColumnData: { + type: "string[]", + label: "Values", + description: "Provide a value for each cell of the row. Google Sheets accepts strings, numbers and boolean values for each cell. To set a cell to an empty value, pass an empty string.", + }, + }; + } + const props = {}; - if (this.hasHeaders) { - const worksheet = await this.getWorksheetById(sheetId, worksheetId); + if (hasHeaders) { + try { + const worksheet = await this.getWorksheetById(sheetId, worksheetId); + const { values } = await this.googleSheets.getSpreadsheetValues(sheetId, `${worksheet?.properties?.title}!1:1`); - const { values } = await this.googleSheets.getSpreadsheetValues(sheetId, `${worksheet?.properties?.title}!1:1`); - if (!values[0]?.length) { - throw new ConfigurationError("Could not find a header row. Please either add headers and click \"Refresh fields\" or adjust the action configuration to continue."); - } - for (let i = 0; i < values[0]?.length; i++) { - props[`col_${i.toString().padStart(4, "0")}`] = { + if (!values?.[0]?.length) { + throw new ConfigurationError("Could not find a header row. Please either add headers and click \"Refresh fields\" or set 'Does the first row of the sheet have headers?' to false."); + } + + for (let i = 0; i < values[0]?.length; i++) { + props[`col_${i.toString().padStart(4, "0")}`] = { + type: "string", + label: values[0][i], + optional: true, + }; + } + props.allColumns = { type: "string", - label: values[0][i], - optional: true, + hidden: true, + default: JSON.stringify(values), + }; + } catch (err) { + console.error("Error fetching headers:", err); + // Fallback to basic column input if headers can't be fetched + return { + headerError: { + type: "string", + label: "Header Fetch Error", + description: `Unable to fetch headers: ${err.message}. Using simple column input instead.`, + optional: true, + hidden: true, + }, + myColumnData: { + type: "string[]", + label: "Values", + description: "Provide a value for each cell of the row. Google Sheets accepts strings, numbers and boolean values for each cell. To set a cell to an empty value, pass an empty string.", + }, }; } - props.allColumns = { - type: "string", - hidden: true, - default: JSON.stringify(values), - }; } else { props.myColumnData = { type: "string[]", @@ -94,7 +135,11 @@ export default { const worksheet = await this.getWorksheetById(sheetId, worksheetId); let cells; - if (this.hasHeaders) { + if (this.hasHeaders + && !isDynamicExpression(sheetId) + && !isDynamicExpression(worksheetId) + && this.allColumns + ) { const rows = JSON.parse(this.allColumns); const [ headers, @@ -103,10 +148,11 @@ export default { .map((_, i) => `col_${i.toString().padStart(4, "0")}`) .map((column) => this[column] ?? ""); } else { + // For dynamic references or no headers, use the array input cells = this.googleSheets.sanitizedArray(this.myColumnData); } - // validate input + // Validate input if (!cells || !cells.length) { throw new ConfigurationError("Please enter an array of elements in `Cells / Column Values`."); } @@ -118,7 +164,7 @@ export default { } const { - arr, + arr: sanitizedCells, convertedIndexes, } = this.googleSheets.arrayValuesToString(cells); @@ -126,7 +172,7 @@ export default { spreadsheetId: sheetId, range: worksheet?.properties?.title, rows: [ - arr, + sanitizedCells, ], }); diff --git a/components/google_sheets/actions/clear-cell/clear-cell.mjs b/components/google_sheets/actions/clear-cell/clear-cell.mjs index 1729280b351b0..faacc119f7c32 100644 --- a/components/google_sheets/actions/clear-cell/clear-cell.mjs +++ b/components/google_sheets/actions/clear-cell/clear-cell.mjs @@ -7,7 +7,7 @@ export default { key: "google_sheets-clear-cell", name: "Clear Cell", description: "Delete the content of a specific cell in a spreadsheet. [See the documentation](https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/clear)", - version: "0.1.9", + version: "0.1.10", type: "action", props: { googleSheets, diff --git a/components/google_sheets/actions/clear-rows/clear-rows.mjs b/components/google_sheets/actions/clear-rows/clear-rows.mjs index 70d6f5c57b7de..0a5d568479186 100644 --- a/components/google_sheets/actions/clear-rows/clear-rows.mjs +++ b/components/google_sheets/actions/clear-rows/clear-rows.mjs @@ -7,7 +7,7 @@ export default { key: "google_sheets-clear-rows", name: "Clear Rows", description: "Delete the content of a row or rows in a spreadsheet. Deleted rows will appear as blank rows. [See the documentation](https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/clear)", - version: "0.1.7", + version: "0.1.8", type: "action", props: { googleSheets, diff --git a/components/google_sheets/actions/common/worksheet.mjs b/components/google_sheets/actions/common/worksheet.mjs index dc368d2158ce2..47a082d112814 100644 --- a/components/google_sheets/actions/common/worksheet.mjs +++ b/components/google_sheets/actions/common/worksheet.mjs @@ -1,8 +1,20 @@ import googleSheets from "../../google_sheets.app.mjs"; +// Helper function to check if a value is a dynamic expression +export const isDynamicExpression = (value) => { + if (!value || typeof value !== "string") return false; + return value.trim().startsWith("{{") && value.trim().endsWith("}}"); +}; + export default { props: { googleSheets, + hasHeaders: { + type: "boolean", + label: "Does the first row of the sheet have headers?", + description: "If the first row of your document has headers, we'll retrieve them to make it easy to enter the value for each column. Note: When using a dynamic reference for the worksheet ID (e.g. `{{steps.foo.$return_value}}`), this setting is ignored.", + reloadProps: true, + }, }, methods: { async getWorksheetById(sheetId, worksheetId) { diff --git a/components/google_sheets/actions/find-row/find-row.mjs b/components/google_sheets/actions/find-row/find-row.mjs index 98178e26c47de..735c67af9264d 100644 --- a/components/google_sheets/actions/find-row/find-row.mjs +++ b/components/google_sheets/actions/find-row/find-row.mjs @@ -7,7 +7,7 @@ export default { key: "google_sheets-find-row", name: "Find Row", description: "Find one or more rows by a column and value. [See the documentation](https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/get)", - version: "0.2.7", + version: "0.2.8", type: "action", props: { googleSheets, diff --git a/components/google_sheets/actions/get-cell/get-cell.mjs b/components/google_sheets/actions/get-cell/get-cell.mjs index 299fc652a1068..145c082892181 100644 --- a/components/google_sheets/actions/get-cell/get-cell.mjs +++ b/components/google_sheets/actions/get-cell/get-cell.mjs @@ -7,7 +7,7 @@ export default { key: "google_sheets-get-cell", name: "Get Cell", description: "Fetch the contents of a specific cell in a spreadsheet. [See the documentation](https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/get)", - version: "0.1.7", + version: "0.1.8", type: "action", props: { googleSheets, diff --git a/components/google_sheets/actions/get-values-in-range/get-values-in-range.mjs b/components/google_sheets/actions/get-values-in-range/get-values-in-range.mjs index 53aa23e893ed3..4a6cc742c6afa 100644 --- a/components/google_sheets/actions/get-values-in-range/get-values-in-range.mjs +++ b/components/google_sheets/actions/get-values-in-range/get-values-in-range.mjs @@ -7,7 +7,7 @@ export default { key: "google_sheets-get-values-in-range", name: "Get Values in Range", description: "Get all values or values from a range of cells using A1 notation. [See the documentation](https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/get)", - version: "0.1.7", + version: "0.1.8", type: "action", props: { googleSheets, diff --git a/components/google_sheets/actions/update-cell/update-cell.mjs b/components/google_sheets/actions/update-cell/update-cell.mjs index 286e721c5581b..46d74a01c3d16 100644 --- a/components/google_sheets/actions/update-cell/update-cell.mjs +++ b/components/google_sheets/actions/update-cell/update-cell.mjs @@ -7,7 +7,7 @@ export default { key: "google_sheets-update-cell", name: "Update Cell", description: "Update a cell in a spreadsheet. [See the documentation](https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/update)", - version: "0.1.7", + version: "0.1.8", type: "action", props: { googleSheets, diff --git a/components/google_sheets/actions/update-multiple-rows/update-multiple-rows.mjs b/components/google_sheets/actions/update-multiple-rows/update-multiple-rows.mjs index 94b1b916a6e16..ccce4af8c10a2 100644 --- a/components/google_sheets/actions/update-multiple-rows/update-multiple-rows.mjs +++ b/components/google_sheets/actions/update-multiple-rows/update-multiple-rows.mjs @@ -9,7 +9,7 @@ export default { key: "google_sheets-update-multiple-rows", name: "Update Multiple Rows", description: "Update multiple rows in a spreadsheet defined by a range. [See the documentation](https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/update)", - version: "0.1.7", + version: "0.1.8", type: "action", props: { googleSheets, diff --git a/components/google_sheets/actions/update-row/update-row.mjs b/components/google_sheets/actions/update-row/update-row.mjs index bd1761f7d80b8..4738114949b4a 100644 --- a/components/google_sheets/actions/update-row/update-row.mjs +++ b/components/google_sheets/actions/update-row/update-row.mjs @@ -1,6 +1,7 @@ import common from "../common/worksheet.mjs"; import { ConfigurationError } from "@pipedream/platform"; import { parseArray } from "../../common/utils.mjs"; +import { isDynamicExpression } from "../common/worksheet.mjs"; const { googleSheets } = common.props; @@ -9,7 +10,7 @@ export default { key: "google_sheets-update-row", name: "Update Row", description: "Update a row in a spreadsheet. [See the documentation](https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/update)", - version: "0.1.8", + version: "0.1.9", type: "action", props: { googleSheets, @@ -29,17 +30,30 @@ export default { }), ], description: "The spreadsheet containing the worksheet to update", + reloadProps: true, }, worksheetId: { propDefinition: [ googleSheets, "worksheetIDs", (c) => ({ - sheetId: c.sheetId, + sheetId: c.sheetId?.value || c.sheetId, }), ], - type: "string", - label: "Worksheet Id", + description: "Select a worksheet or enter a custom expression. When referencing a spreadsheet dynamically, you must provide a custom expression for the worksheet.", + async options({ sheetId }) { + // If sheetId is a dynamic reference, don't load options + if (isDynamicExpression(sheetId)) { + return []; + } + + // Otherwise, call the original options function with the correct context + const origOptions = googleSheets.propDefinitions.worksheetIDs.options; + return origOptions.call(this, { + sheetId, + }); + }, + reloadProps: true, }, row: { propDefinition: [ @@ -47,12 +61,7 @@ export default { "row", ], }, - hasHeaders: { - type: "boolean", - label: "Does the first row of the sheet have headers?", - description: "If the first row of your document has headers, we'll retrieve them to make it easy to enter the value for each column. Please note, that if you are referencing a worksheet using a custom expression referencing data from another step, e.g. `{{steps.my_step.$return_value}}` this prop cannot be used. If you want to retrieve the header row, select both **Spreadsheet** and **Worksheet ID** from the dropdowns above.", - reloadProps: true, - }, + hasHeaders: common.props.hasHeaders, }, async additionalProps() { const { @@ -62,32 +71,63 @@ export default { hasHeaders, } = this; + // If using dynamic expressions for either sheetId or worksheetId, return only array input + if (isDynamicExpression(sheetId) || isDynamicExpression(worksheetId)) { + return { + myColumnData: { + type: "string[]", + label: "Values", + description: "Provide a value for each cell of the row. Google Sheets accepts strings, numbers and boolean values for each cell. To set a cell to an empty value, pass an empty string.", + }, + }; + } + const props = {}; if (hasHeaders && row) { - const worksheet = await this.getWorksheetById(sheetId, worksheetId); + try { + const worksheet = await this.getWorksheetById(sheetId, worksheetId); + const { values } = await this.googleSheets.getSpreadsheetValues(sheetId, `${worksheet?.properties?.title}!1:1`); - const { values } = await this.googleSheets.getSpreadsheetValues(sheetId, `${worksheet?.properties?.title}!1:1`); - if (!values[0]?.length) { - throw new ConfigurationError("Could not find a header row. Please either add headers and click \"Refresh fields\" or adjust the action configuration to continue."); - } - const { values: rowValues } = !isNaN(row) - ? await this.googleSheets.getSpreadsheetValues(sheetId, `${worksheet?.properties?.title}!${row}:${row}`) - : {}; - for (let i = 0; i < values[0]?.length; i++) { - props[`col_${i.toString().padStart(4, "0")}`] = { + if (!values?.[0]?.length) { + throw new ConfigurationError("Could not find a header row. Please either add headers and click \"Refresh fields\" or set 'Does the first row of the sheet have headers?' to false."); + } + + const { values: rowValues } = !isNaN(row) + ? await this.googleSheets.getSpreadsheetValues(sheetId, `${worksheet?.properties?.title}!${row}:${row}`) + : {}; + + for (let i = 0; i < values[0]?.length; i++) { + props[`col_${i.toString().padStart(4, "0")}`] = { + type: "string", + label: values[0][i], + optional: true, + default: rowValues?.[0]?.[i], + }; + } + props.allColumns = { type: "string", - label: values[0][i], - optional: true, - default: rowValues?.[0]?.[i], + hidden: true, + default: JSON.stringify(values), + }; + } catch (err) { + console.error("Error fetching headers:", err); + // Fallback to basic column input if headers can't be fetched + return { + headerError: { + type: "string", + label: "Header Fetch Error", + description: `Unable to fetch headers: ${err.message}. Using simple column input instead.`, + optional: true, + hidden: true, + }, + myColumnData: { + type: "string[]", + label: "Values", + description: "Provide a value for each cell of the row. Google Sheets accepts strings, numbers and boolean values for each cell. To set a cell to an empty value, pass an empty string.", + }, }; } - props.allColumns = { - type: "string", - hidden: true, - default: JSON.stringify(values), - }; - } - if (hasHeaders === false) { + } else { props.myColumnData = { type: "string[]", label: "Values", @@ -97,8 +137,19 @@ export default { return props; }, async run() { + const { + sheetId, + worksheetId, + row, + } = this; + let cells; - if (this.hasHeaders) { + if (this.hasHeaders + && !isDynamicExpression(sheetId) + && !isDynamicExpression(worksheetId) + && this.allColumns + ) { + // Only use header-based processing if we have the allColumns prop and no dynamic expressions const rows = JSON.parse(this.allColumns); const [ headers, @@ -107,6 +158,7 @@ export default { .map((_, i) => `col_${i.toString().padStart(4, "0")}`) .map((column) => this[column] ?? ""); } else { + // For dynamic references or no headers, use the array input cells = this.googleSheets.sanitizedArray(this.myColumnData); } @@ -121,10 +173,11 @@ export default { if (Array.isArray(cells[0])) { throw new ConfigurationError("Row Values is a multi-dimensional array. A one-dimensional is expected."); } - const worksheet = await this.getWorksheetById(this.sheetId, this.worksheetId); + + const worksheet = await this.getWorksheetById(sheetId, worksheetId); const request = { - spreadsheetId: this.sheetId, - range: `${worksheet?.properties?.title}!${this.row}:${this.row}`, + spreadsheetId: sheetId, + range: `${worksheet?.properties?.title}!${row}:${row}`, valueInputOption: "USER_ENTERED", resource: { values: [ @@ -132,6 +185,7 @@ export default { ], }, }; + return await this.googleSheets.updateSpreadsheet(request); }, }; diff --git a/components/google_sheets/actions/upsert-row/upsert-row.mjs b/components/google_sheets/actions/upsert-row/upsert-row.mjs index b0bd80a0bcbf2..904bf770ae3eb 100644 --- a/components/google_sheets/actions/upsert-row/upsert-row.mjs +++ b/components/google_sheets/actions/upsert-row/upsert-row.mjs @@ -24,7 +24,7 @@ export default { key: "google_sheets-upsert-row", name: "Upsert Row", description: "Upsert a row of data in a Google Sheet. [See the documentation](https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/append)", - version: "0.1.9", + version: "0.1.10", type: "action", props: { googleSheets, diff --git a/components/google_sheets/package.json b/components/google_sheets/package.json index 9fe8e7e358c9d..061603797ee13 100644 --- a/components/google_sheets/package.json +++ b/components/google_sheets/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/google_sheets", - "version": "0.7.8", + "version": "0.7.9", "description": "Pipedream Google_sheets Components", "main": "google_sheets.app.mjs", "keywords": [