Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 73 additions & 27 deletions components/google_sheets/actions/add-single-row/add-single-row.mjs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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,
Expand All @@ -27,6 +28,7 @@ export default {
driveId: googleSheets.methods.getDriveId(c.drive),
}),
],
reloadProps: true,
},
worksheetId: {
propDefinition: [
Expand All @@ -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[]",
Expand All @@ -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,
Expand All @@ -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`.");
}
Expand All @@ -118,15 +164,15 @@ export default {
}

const {
arr,
arr: sanitizedCells,
convertedIndexes,
} = this.googleSheets.arrayValuesToString(cells);

const data = await this.googleSheets.addRowsToSheet({
spreadsheetId: sheetId,
range: worksheet?.properties?.title,
rows: [
arr,
sanitizedCells,
],
});

Expand Down
12 changes: 12 additions & 0 deletions components/google_sheets/actions/common/worksheet.mjs
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down
120 changes: 87 additions & 33 deletions components/google_sheets/actions/update-row/update-row.mjs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -29,30 +30,38 @@ 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: [
googleSheets,
"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 {
Expand All @@ -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",
Expand All @@ -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,
Expand All @@ -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);
}

Expand All @@ -121,17 +173,19 @@ 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: [
cells,
],
},
};

return await this.googleSheets.updateSpreadsheet(request);
},
};
2 changes: 1 addition & 1 deletion components/google_sheets/package.json
Original file line number Diff line number Diff line change
@@ -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": [
Expand Down
Loading