diff --git a/components/google_sheets/actions/add-conditional-format-rule/add-conditional-format-rule.mjs b/components/google_sheets/actions/add-conditional-format-rule/add-conditional-format-rule.mjs new file mode 100644 index 0000000000000..4abe7ae7d062a --- /dev/null +++ b/components/google_sheets/actions/add-conditional-format-rule/add-conditional-format-rule.mjs @@ -0,0 +1,223 @@ +import googleSheets from "../../google_sheets.app.mjs"; +import { ConfigurationError } from "@pipedream/platform"; + +export default { + key: "google_sheets-add-conditional-format-rule", + name: "Add Conditional Format Rule", + description: "Create conditional formatting with color scales or custom formulas. [See the documentation](https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#AddConditionalFormatRuleRequest)", + version: "0.0.1", + type: "action", + annotations: { + destructiveHint: false, + openWorldHint: true, + readOnlyHint: false, + }, + props: { + googleSheets, + drive: { + propDefinition: [ + googleSheets, + "watchedDrive", + ], + }, + sheetId: { + propDefinition: [ + googleSheets, + "sheetID", + (c) => ({ + driveId: googleSheets.methods.getDriveId(c.drive), + }), + ], + }, + worksheetId: { + propDefinition: [ + googleSheets, + "worksheetIDs", + (c) => ({ + sheetId: c.sheetId, + }), + ], + }, + range: { + propDefinition: [ + googleSheets, + "range", + ], + description: "The range of cells to format (e.g., `A1:A10`)", + }, + conditionType: { + type: "string", + label: "Validation Type", + description: "The type of data condition", + options: [ + "ONE_OF_LIST", + "NUMBER_GREATER", + "NUMBER_LESS", + "DATE_BEFORE", + "DATE_AFTER", + "TEXT_CONTAINS", + "TEXT_IS_EMAIL", + "TEXT_IS_URL", + "BOOLEAN", + ], + }, + conditionValues: { + type: "string[]", + label: "Condition Values", + description: "Values for condition (e.g., color scales or custom formulas)", + }, + formattingType: { + type: "string", + label: "Formatting Type", + description: "Choose between boolean condition or gradient color scale", + options: [ + "BOOLEAN_RULE", + "GRADIENT_RULE", + ], + default: "BOOLEAN_RULE", + }, + rgbColor: { + type: "object", + label: "RGB Color", + description: "The RGB color value (e.g., {\"red\": 1.0, \"green\": 0.5, \"blue\": 0.2})", + optional: true, + }, + textFormat: { + type: "object", + label: "Text Format", + description: "The text format options", + optional: true, + }, + bold: { + type: "boolean", + label: "Bold", + description: "Whether the text is bold", + optional: true, + }, + italic: { + type: "boolean", + label: "Italic", + description: "Whether the text is italic", + optional: true, + }, + strikethrough: { + type: "boolean", + label: "Strikethrough", + description: "Whether the text is strikethrough", + optional: true, + }, + interpolationPointType: { + type: "string", + label: "Interpolation Point Type", + description: "The interpolation point type", + options: [ + "MIN", + "MAX", + "NUMBER", + "PERCENT", + "PERCENTILE", + ], + optional: true, + }, + index: { + type: "integer", + label: "Index", + description: "The zero-based index of the rule", + }, + }, + async run({ $ }) { + const { + startCol, + endCol, + startRow, + endRow, + } = this.googleSheets._parseRangeString(`${this.worksheetId}!${this.range}`); + + const rule = { + ranges: [ + { + sheetId: this.worksheetId, + startRowIndex: startRow, + endRowIndex: endRow, + startColumnIndex: startCol.charCodeAt(0) - 65, + endColumnIndex: endCol.charCodeAt(0) - 64, + }, + ], + }; + + const parseRgbColor = (rgbColor = {}) => { + if (typeof rgbColor === "string") { + try { + rgbColor = JSON.parse(rgbColor); + } catch { + throw new ConfigurationError("Could not parse RGB Color. Please provide a valid JSON object."); + } + } + return rgbColor; + }; + + this.formattingType === "GRADIENT_RULE" ? + rule.gradientRule = { + minpoint: { + colorStyle: { + rgbColor: parseRgbColor(this.rgbColor), + }, + type: this.interpolationPointType, + value: "MIN", + }, + midpoint: { + colorStyle: { + rgbColor: parseRgbColor(this.rgbColor), + }, + type: this.interpolationPointType, + value: "MID", + }, + maxpoint: { + colorStyle: { + rgbColor: parseRgbColor(this.rgbColor), + }, + type: this.interpolationPointType, + value: "MAX", + }, + } : + rule.booleanRule = { + condition: { + type: this.conditionType, + values: this.conditionValues?.map((v) => ({ + userEnteredValue: v, + })) || [], + }, + format: { + backgroundColorStyle: { + rgbColor: parseRgbColor(this.rgbColor), + }, + textFormat: { + ...this.textFormat, + foregroundColorStyle: { + rgbColor: parseRgbColor(this.rgbColor), + }, + bold: this.bold, + italic: this.italic, + strikethrough: this.strikethrough, + }, + }, + }; + + const request = { + spreadsheetId: this.sheetId, + requestBody: { + requests: [ + { + addConditionalFormatRule: { + rule, + index: this.index, + }, + }, + ], + }, + }; + const response = await this.googleSheets.batchUpdate(request); + $.export("$summary", "Successfully added conditional format rule."); + return response; + }, +}; diff --git a/components/google_sheets/actions/add-protected-range/add-protected-range.mjs b/components/google_sheets/actions/add-protected-range/add-protected-range.mjs new file mode 100644 index 0000000000000..374cfe7a9ddb3 --- /dev/null +++ b/components/google_sheets/actions/add-protected-range/add-protected-range.mjs @@ -0,0 +1,111 @@ +import googleSheets from "../../google_sheets.app.mjs"; + +export default { + key: "google_sheets-add-protected-range", + name: "Add Protected Range", + description: "Add edit protection to cell range with permissions. [See the documentation](https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#AddProtectedRangeRequest)", + version: "0.0.1", + type: "action", + annotations: { + destructiveHint: false, + openWorldHint: true, + readOnlyHint: false, + }, + props: { + googleSheets, + drive: { + propDefinition: [ + googleSheets, + "watchedDrive", + ], + }, + sheetId: { + propDefinition: [ + googleSheets, + "sheetID", + (c) => ({ + driveId: googleSheets.methods.getDriveId(c.drive), + }), + ], + }, + worksheetId: { + propDefinition: [ + googleSheets, + "worksheetIDs", + (c) => ({ + sheetId: c.sheetId, + }), + ], + }, + protectedRangeId: { + type: "integer", + label: "Protected Range ID", + description: "The ID of the protected range (required for update and delete operations). This is a unique identifier assigned by Google Sheets", + optional: true, + }, + range: { + propDefinition: [ + googleSheets, + "range", + ], + description: "The range of cells to protect (e.g., `A1:A10`). Required for add and update operations", + }, + description: { + type: "string", + label: "Description", + description: "A description of the protected range", + optional: true, + }, + requestingUserCanEdit: { + type: "boolean", + label: "Requesting User Can Edit", + description: "If true, the user making this request can edit the protected range", + optional: true, + default: false, + }, + protectors: { + type: "string[]", + label: "Protectors", + description: "Email addresses of users/groups who can edit the protected range (e.g., user@example.com)", + optional: true, + }, + }, + async run({ $ }) { + const { + startCol, + endCol, + startRow, + endRow, + } = this.googleSheets._parseRangeString(`${this.worksheetId}!${this.range}`); + + const request = { + spreadsheetId: this.sheetId, + requestBody: { + requests: [ + { + addProtectedRange: { + protectedRange: { + protectedRangeId: this.protectedRangeId, + range: { + sheetId: this.worksheetId, + startRowIndex: startRow, + endRowIndex: endRow, + startColumnIndex: startCol.charCodeAt(0) - 65, + endColumnIndex: endCol.charCodeAt(0) - 64, + }, + description: this.description, + requestingUserCanEdit: this.requestingUserCanEdit, + editors: { + users: this.protectors || [], + }, + }, + }, + }, + ], + }, + }; + const response = await this.googleSheets.batchUpdate(request); + $.export("$summary", "Successfully added protected range."); + return response; + }, +}; diff --git a/components/google_sheets/actions/delete-conditional-format-rule/delete-conditional-format-rule.mjs b/components/google_sheets/actions/delete-conditional-format-rule/delete-conditional-format-rule.mjs new file mode 100644 index 0000000000000..5fff05f0293e0 --- /dev/null +++ b/components/google_sheets/actions/delete-conditional-format-rule/delete-conditional-format-rule.mjs @@ -0,0 +1,64 @@ +import googleSheets from "../../google_sheets.app.mjs"; + +export default { + key: "google_sheets-delete-conditional-format-rule", + name: "Delete Conditional Format Rule", + description: "Remove conditional formatting rule by index. [See the documentation](https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#DeleteConditionalFormatRuleRequest)", + version: "0.0.1", + type: "action", + annotations: { + destructiveHint: true, + openWorldHint: true, + readOnlyHint: false, + }, + props: { + googleSheets, + drive: { + propDefinition: [ + googleSheets, + "watchedDrive", + ], + }, + sheetId: { + propDefinition: [ + googleSheets, + "sheetID", + (c) => ({ + driveId: googleSheets.methods.getDriveId(c.drive), + }), + ], + }, + worksheetId: { + propDefinition: [ + googleSheets, + "worksheetIDs", + (c) => ({ + sheetId: c.sheetId, + }), + ], + }, + index: { + type: "integer", + label: "Index", + description: "The zero-based index of the rule", + }, + }, + async run({ $ }) { + const request = { + spreadsheetId: this.sheetId, + requestBody: { + requests: [ + { + deleteConditionalFormatRule: { + sheetId: this.worksheetId, + index: this.index, + }, + }, + ], + }, + }; + const response = await this.googleSheets.batchUpdate(request); + $.export("$summary", "Successfully deleted conditional format rule."); + return response; + }, +}; diff --git a/components/google_sheets/actions/merge-cells/merge-cells.mjs b/components/google_sheets/actions/merge-cells/merge-cells.mjs new file mode 100644 index 0000000000000..d4312f374856c --- /dev/null +++ b/components/google_sheets/actions/merge-cells/merge-cells.mjs @@ -0,0 +1,90 @@ +import googleSheets from "../../google_sheets.app.mjs"; + +export default { + key: "google_sheets-merge-cells", + name: "Merge Cells", + description: "Merge a range of cells into a single cell. [See the documentation](https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#MergeCellsRequest)", + version: "0.0.1", + type: "action", + annotations: { + destructiveHint: false, + openWorldHint: true, + readOnlyHint: false, + }, + props: { + googleSheets, + drive: { + propDefinition: [ + googleSheets, + "watchedDrive", + ], + }, + sheetId: { + propDefinition: [ + googleSheets, + "sheetID", + (c) => ({ + driveId: googleSheets.methods.getDriveId(c.drive), + }), + ], + }, + worksheetId: { + propDefinition: [ + googleSheets, + "worksheetIDs", + (c) => ({ + sheetId: c.sheetId, + }), + ], + }, + range: { + propDefinition: [ + googleSheets, + "range", + ], + description: "The range of cells to apply validation (e.g., `A1:A10`)", + }, + mergeType: { + type: "string", + label: "Merge Type", + description: "The type of merge to perform", + options: [ + "MERGE_ALL", + "MERGE_COLUMNS", + "MERGE_ROWS", + ], + default: "MERGE_ALL", + }, + }, + async run({ $ }) { + const { + startCol, + endCol, + startRow, + endRow, + } = this.googleSheets._parseRangeString(`${this.worksheetId}!${this.range}`); + + const request = { + spreadsheetId: this.sheetId, + requestBody: { + requests: [ + { + mergeCells: { + range: { + sheetId: this.worksheetId, + startRowIndex: startRow, + endRowIndex: endRow, + startColumnIndex: startCol.charCodeAt(0) - 65, + endColumnIndex: endCol.charCodeAt(0) - 64, + }, + mergeType: this.mergeType, + }, + }, + ], + }, + }; + const response = await this.googleSheets.batchUpdate(request); + $.export("$summary", "Successfully merged cells."); + return response; + }, +}; diff --git a/components/google_sheets/actions/set-data-validation/set-data-validation.mjs b/components/google_sheets/actions/set-data-validation/set-data-validation.mjs new file mode 100644 index 0000000000000..00bae5364bb73 --- /dev/null +++ b/components/google_sheets/actions/set-data-validation/set-data-validation.mjs @@ -0,0 +1,122 @@ +import googleSheets from "../../google_sheets.app.mjs"; + +export default { + key: "google_sheets-set-data-validation", + name: "Set Data Validation", + description: "Add data validation rules to cells (dropdowns, checkboxes, date/number validation). [See the documentation](https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#SetDataValidationRequest)", + version: "0.0.1", + type: "action", + annotations: { + destructiveHint: false, + openWorldHint: true, + readOnlyHint: false, + }, + props: { + googleSheets, + drive: { + propDefinition: [ + googleSheets, + "watchedDrive", + ], + }, + sheetId: { + propDefinition: [ + googleSheets, + "sheetID", + (c) => ({ + driveId: googleSheets.methods.getDriveId(c.drive), + }), + ], + }, + worksheetId: { + propDefinition: [ + googleSheets, + "worksheetIDs", + (c) => ({ + sheetId: c.sheetId, + }), + ], + }, + range: { + propDefinition: [ + googleSheets, + "range", + ], + description: "The range of cells to apply validation (e.g., `A1:A10`)", + }, + validationType: { + type: "string", + label: "Validation Type", + description: "The type of data validation", + options: [ + "NUMBER_GREATER", + "NUMBER_GREATER_THAN_EQ", + "NUMBER_LESS_THAN_EQ", + "NUMBER_LESS", + "TEXT_CONTAINS", + "TEXT_NOT_CONTAINS", + "DATE_EQUAL_TO", + "ONE_OF_LIST", + "DATE_AFTER", + "DATE_ON_OR_AFTER", + "DATE_BEFORE", + "DATE_ON_OR_BEFORE", + "DATE_BETWEEN", + "TEXT_STARTS_WITH", + "TEXT_ENDS_WITH", + "TEXT_EQUAL_TO", + "TEXT_NOT_EQUAL_TO", + "CUSTOM_FORMULA", + "NUMBER_EQUAL_TO", + "NUMBER_NOT_EQUAL_TO", + "NUMBER_BETWEEN", + "NUMBER_NOT_BETWEEN", + ], + }, + validationValues: { + type: "string[]", + label: "Validation Values", + description: "Values for validation (e.g., dropdown options)", + }, + }, + async run({ $ }) { + const { + startCol, + endCol, + startRow, + endRow, + } = this.googleSheets._parseRangeString(`${this.worksheetId}!${this.range}`); + + const request = { + spreadsheetId: this.sheetId, + requestBody: { + requests: [ + { + setDataValidation: { + range: { + sheetId: this.worksheetId, + startRowIndex: startRow, + endRowIndex: endRow, + startColumnIndex: startCol.charCodeAt(0) - 65, + endColumnIndex: endCol.charCodeAt(0) - 64, + }, + rule: { + condition: { + type: this.validationType, + values: this.validationValues?.map((v) => ({ + userEnteredValue: v, + })) || [], + }, + showCustomUi: true, + strict: true, + }, + }, + }, + ], + }, + }; + const response = await this.googleSheets.batchUpdate(request); + $.export("$summary", "Successfully set data validation."); + return response; + }, +}; diff --git a/components/google_sheets/actions/update-conditional-format-rule/update-conditional-format-rule.mjs b/components/google_sheets/actions/update-conditional-format-rule/update-conditional-format-rule.mjs new file mode 100644 index 0000000000000..ff8357f0df9a9 --- /dev/null +++ b/components/google_sheets/actions/update-conditional-format-rule/update-conditional-format-rule.mjs @@ -0,0 +1,238 @@ +import googleSheets from "../../google_sheets.app.mjs"; +import { ConfigurationError } from "@pipedream/platform"; + +export default { + key: "google_sheets-update-conditional-format-rule", + name: "Update Conditional Format Rule", + description: "Modify existing conditional formatting rule. [See the documentation](https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets/request#UpdateConditionalFormatRuleRequest)", + version: "0.0.1", + type: "action", + annotations: { + destructiveHint: false, + openWorldHint: true, + readOnlyHint: false, + }, + props: { + googleSheets, + drive: { + propDefinition: [ + googleSheets, + "watchedDrive", + ], + }, + sheetId: { + propDefinition: [ + googleSheets, + "sheetID", + (c) => ({ + driveId: googleSheets.methods.getDriveId(c.drive), + }), + ], + }, + worksheetId: { + propDefinition: [ + googleSheets, + "worksheetIDs", + (c) => ({ + sheetId: c.sheetId, + }), + ], + }, + range: { + propDefinition: [ + googleSheets, + "range", + ], + description: "The range of cells to protect (e.g., `A1:A10`)", + }, + conditionType: { + type: "string", + label: "Validation Type", + description: "The type of data condition", + options: [ + "ONE_OF_LIST", + "NUMBER_GREATER", + "NUMBER_LESS", + "DATE_BEFORE", + "DATE_AFTER", + "TEXT_CONTAINS", + "TEXT_IS_EMAIL", + "TEXT_IS_URL", + "BOOLEAN", + ], + }, + conditionValues: { + type: "string[]", + label: "Condition Values", + description: "Values for condition (e.g., color scales or custom formulas)", + }, + formattingType: { + type: "string", + label: "Formatting Type", + description: "Choose between boolean condition or gradient color scale", + options: [ + "BOOLEAN_RULE", + "GRADIENT_RULE", + ], + default: "BOOLEAN_RULE", + }, + rgbColor: { + type: "object", + label: "RGB Color", + description: "The RGB color value (e.g., {\"red\": 1.0, \"green\": 0.5, \"blue\": 0.2})", + optional: true, + }, + textFormat: { + type: "object", + label: "Text Format", + description: "The text format options", + optional: true, + }, + bold: { + type: "boolean", + label: "Bold", + description: "Whether the text is bold", + optional: true, + }, + italic: { + type: "boolean", + label: "Italic", + description: "Whether the text is italic", + optional: true, + }, + strikethrough: { + type: "boolean", + label: "Strikethrough", + description: "Whether the text is strikethrough", + optional: true, + }, + interpolationPointType: { + type: "string", + label: "Interpolation Point Type", + description: "The interpolation point type", + options: [ + "MIN", + "MAX", + "NUMBER", + "PERCENT", + "PERCENTILE", + ], + optional: true, + }, + index: { + type: "integer", + label: "Index", + description: "The zero-based index of the rule", + }, + newIndex: { + type: "integer", + label: "New Index", + description: "The new zero-based index of the rule", + optional: true, + }, + }, + async run({ $ }) { + const { + startCol, + endCol, + startRow, + endRow, + } = this.googleSheets._parseRangeString(`${this.worksheetId}!${this.range}`); + + const rule = { + ranges: [ + { + sheetId: this.worksheetId, + startRowIndex: startRow, + endRowIndex: endRow, + startColumnIndex: startCol.charCodeAt(0) - 65, + endColumnIndex: endCol.charCodeAt(0) - 64, + }, + ], + }; + const parseRgbColor = (rgbColor = {}) => { + if (typeof rgbColor === "string") { + try { + rgbColor = JSON.parse(rgbColor); + } catch { + throw new ConfigurationError("Could not parse RGB Color. Please provide a valid JSON object."); + } + } + return rgbColor; + }; + + this.formattingType === "GRADIENT_RULE" ? + rule.gradientRule = { + minpoint: { + interpolationPoint: { + colorStyle: { + rgbColor: parseRgbColor(this.rgbColor), + }, + type: this.InterpolationPointType, + value: "MIN", + }, + }, + midpoint: { + interpolationPoint: { + colorStyle: { + rgbColor: parseRgbColor(this.rgbColor), + }, + type: this.InterpolationPointType, + value: "MID", + }, + }, + maxpoint: { + interpolationPoint: { + colorStyle: { + rgbColor: parseRgbColor(this.rgbColor), + }, + type: this.InterpolationPointType, + value: "MAX", + }, + }, + } : + rule.booleanRule = { + condition: { + type: this.conditionType, + values: this.conditionValues?.map((v) => ({ + userEnteredValue: v, + })) || [], + }, + format: { + backgroundColorStyle: { + rgbColor: parseRgbColor(this.rgbColor), + }, + textFormat: { + ...this.textFormat, + foregroundColorStyle: { + rgbColor: parseRgbColor(this.rgbColor), + + }, + bold: this.bold, + italic: this.italic, + strikethrough: this.strikethrough, + }, + }, + }; + + const request = { + spreadsheetId: this.sheetId, + requestBody: { + requests: [ + { + updateConditionalFormatRule: { + index: this.index, + sheetId: this.worksheetId, + rule, + newIndex: this.newIndex, + }, + }, + ], + }, + }; + + const response = await this.googleSheets.batchUpdate(request); + $.export("$summary", "Successfully updated conditional format rule."); + return response; + }, +}; diff --git a/components/google_sheets/package.json b/components/google_sheets/package.json index 0b0021b697c69..65bc7365d07b2 100644 --- a/components/google_sheets/package.json +++ b/components/google_sheets/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/google_sheets", - "version": "0.10.0", + "version": "0.11.0", "description": "Pipedream Google_sheets Components", "main": "google_sheets.app.mjs", "keywords": [