@guayaba/workflow-piece-google-sheets 0.14.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/.babelrc +3 -0
  2. package/.eslintrc.json +18 -0
  3. package/README.md +5 -0
  4. package/assets/logo.png +0 -0
  5. package/package.json +28 -0
  6. package/src/i18n/ar.json +124 -0
  7. package/src/i18n/bg.json +124 -0
  8. package/src/i18n/ca.json +132 -0
  9. package/src/i18n/de.json +165 -0
  10. package/src/i18n/es.json +165 -0
  11. package/src/i18n/fr.json +165 -0
  12. package/src/i18n/hi.json +132 -0
  13. package/src/i18n/hu.json +124 -0
  14. package/src/i18n/hy.json +124 -0
  15. package/src/i18n/id.json +132 -0
  16. package/src/i18n/it.json +124 -0
  17. package/src/i18n/ja.json +165 -0
  18. package/src/i18n/ko.json +124 -0
  19. package/src/i18n/nl.json +165 -0
  20. package/src/i18n/pl.json +124 -0
  21. package/src/i18n/pt.json +165 -0
  22. package/src/i18n/ru.json +132 -0
  23. package/src/i18n/sv.json +124 -0
  24. package/src/i18n/translation.json +165 -0
  25. package/src/i18n/uk.json +124 -0
  26. package/src/i18n/vi.json +132 -0
  27. package/src/i18n/zh.json +165 -0
  28. package/src/index.ts +93 -0
  29. package/src/lib/actions/clear-sheet.ts +60 -0
  30. package/src/lib/actions/copy-worksheet.ts +32 -0
  31. package/src/lib/actions/create-column.ts +109 -0
  32. package/src/lib/actions/create-spreadsheet.ts +122 -0
  33. package/src/lib/actions/create-worksheet.ts +62 -0
  34. package/src/lib/actions/delete-row.action.ts +40 -0
  35. package/src/lib/actions/delete-worksheet.ts +36 -0
  36. package/src/lib/actions/export-sheet.ts +86 -0
  37. package/src/lib/actions/find-row-by-num.ts +42 -0
  38. package/src/lib/actions/find-rows.ts +135 -0
  39. package/src/lib/actions/find-spreadsheets.ts +83 -0
  40. package/src/lib/actions/find-worksheet.ts +52 -0
  41. package/src/lib/actions/format-spreadsheet-row.ts +112 -0
  42. package/src/lib/actions/get-many-rows.ts +42 -0
  43. package/src/lib/actions/get-rows.ts +207 -0
  44. package/src/lib/actions/insert-multiple-rows.action.ts +542 -0
  45. package/src/lib/actions/insert-row.action.ts +111 -0
  46. package/src/lib/actions/rename-worksheet.ts +44 -0
  47. package/src/lib/actions/update-multiple-rows.ts +177 -0
  48. package/src/lib/actions/update-row.ts +93 -0
  49. package/src/lib/common/common.ts +383 -0
  50. package/src/lib/common/props.ts +274 -0
  51. package/src/lib/triggers/helpers.ts +155 -0
  52. package/src/lib/triggers/new-or-updated-row.trigger.ts +299 -0
  53. package/src/lib/triggers/new-row-added-webhook.ts +182 -0
  54. package/src/lib/triggers/new-spreadsheet.ts +88 -0
  55. package/src/lib/triggers/new-worksheet.ts +96 -0
  56. package/tsconfig.json +16 -0
  57. package/tsconfig.lib.json +15 -0
@@ -0,0 +1,542 @@
1
+ import { googleSheetsAuth } from '../common/common';
2
+ import {
3
+ createAction,
4
+ DropdownOption,
5
+ DynamicPropsValue,
6
+ OAuth2PropertyValue,
7
+ Property,
8
+ } from '@guayaba/workflows-framework';
9
+ import {
10
+ Dimension,
11
+ googleSheetsCommon,
12
+ objectToArray,
13
+ ValueInputOption,
14
+ columnToLabel,
15
+ areSheetIdsValid,
16
+ GoogleSheetsAuthValue,
17
+ createGoogleClient,
18
+ } from '../common/common';
19
+ import { getWorkSheetName, getWorkSheetGridSize } from '../triggers/helpers';
20
+ import { google, sheets_v4 } from 'googleapis';
21
+ import { MarkdownVariant } from '@guayaba/workflows-shared';
22
+ import { parse } from 'csv-parse/sync';
23
+ import { commonProps } from '../common/props';
24
+
25
+ type RowValueType = Record<string, any>;
26
+
27
+ export const insertMultipleRowsAction = createAction({
28
+ auth: googleSheetsAuth,
29
+ name: 'google-sheets-insert-multiple-rows',
30
+ displayName: 'Add Multiple Rows',
31
+ description: 'Add multiple rows of data at once to a specific spreadsheet.',
32
+ props: {
33
+ ...commonProps,
34
+ input_type: Property.StaticDropdown({
35
+ displayName: 'Rows Data Format',
36
+ description: 'Select the format of the input values to be added into the worksheet.',
37
+ required: true,
38
+ defaultValue: 'column_names',
39
+ options: {
40
+ disabled: false,
41
+ options: [
42
+ {
43
+ value: 'csv',
44
+ label: 'CSV',
45
+ },
46
+ {
47
+ value: 'json',
48
+ label: 'JSON',
49
+ },
50
+ {
51
+ value: 'column_names',
52
+ label: 'Column Names',
53
+ },
54
+ ],
55
+ },
56
+ }),
57
+ values: Property.DynamicProperties({
58
+ auth: googleSheetsAuth,
59
+ displayName: 'Values',
60
+ description: 'The values to add.',
61
+ required: true,
62
+ refreshers: ['sheetId', 'spreadsheetId', 'input_type', 'headerRow'],
63
+ props: async ({ auth, sheetId, spreadsheetId, input_type, headerRow }) => {
64
+ const sheet_id = Number(sheetId);
65
+ const spreadsheet_id = spreadsheetId as unknown as string;
66
+ const valuesInputType = input_type as unknown as string;
67
+
68
+ if (
69
+ !auth ||
70
+ (spreadsheet_id ?? '').toString().length === 0 ||
71
+ (sheet_id ?? '').toString().length === 0 ||
72
+ !['csv', 'json', 'column_names'].includes(valuesInputType)
73
+ ) {
74
+ return {};
75
+ }
76
+
77
+ const fields: DynamicPropsValue = {};
78
+
79
+ switch (valuesInputType) {
80
+ case 'csv':
81
+ fields['markdown'] = Property.MarkDown({
82
+ value: `Ensure the first row contains column headers that match the sheet's column names.`,
83
+ variant: MarkdownVariant.INFO,
84
+ });
85
+ fields['values'] = Property.LongText({
86
+ displayName: 'CSV',
87
+ required: true,
88
+ });
89
+ break;
90
+ case 'json':
91
+ fields['markdown'] = Property.MarkDown({
92
+ value: `Provide values in JSON format. Ensure the column names match the sheet's header.`,
93
+ variant: MarkdownVariant.INFO,
94
+ });
95
+ fields['values'] = Property.Json({
96
+ displayName: 'JSON',
97
+ required: true,
98
+ defaultValue: [
99
+ {
100
+ column1: 'value1',
101
+ column2: 'value2',
102
+ },
103
+ {
104
+ column1: 'value3',
105
+ column2: 'value4',
106
+ },
107
+ ],
108
+ });
109
+ break;
110
+ case 'column_names': {
111
+ const headers = await googleSheetsCommon.getGoogleSheetRows({
112
+ spreadsheetId: spreadsheet_id,
113
+ auth: auth,
114
+ sheetId: sheet_id,
115
+ rowIndex_s: 1,
116
+ rowIndex_e: 1,
117
+ headerRow: (headerRow as unknown as number) || 1,
118
+ });
119
+ const firstRow = headers[0].values ?? {};
120
+
121
+ //check for empty headers
122
+ if (Object.keys(firstRow).length === 0) {
123
+ fields['markdown'] = Property.MarkDown({
124
+ value: `We couldn't find any headers in the selected spreadsheet or worksheet. Please add headers to the sheet and refresh the page to reflect the columns.`,
125
+ variant: MarkdownVariant.INFO,
126
+ });
127
+ } else {
128
+ const columns: {
129
+ [key: string]: any;
130
+ } = {};
131
+
132
+ for (const key in firstRow) {
133
+ columns[key] = Property.ShortText({
134
+ displayName: firstRow[key],
135
+ // description: firstRow[key],
136
+ required: false,
137
+ defaultValue: '',
138
+ });
139
+ }
140
+ fields['values'] = Property.Array({
141
+ displayName: 'Values',
142
+ required: false,
143
+ properties: columns,
144
+ });
145
+ }
146
+ }
147
+ }
148
+ return fields;
149
+ },
150
+ }),
151
+ overwrite: Property.Checkbox({
152
+ displayName: 'Overwrite Existing Data?',
153
+ description:
154
+ 'Enable this option to replace all existing data in the sheet with new data from your input. This will clear any extra rows beyond the updated range.',
155
+ required: false,
156
+ defaultValue: false,
157
+ }),
158
+ check_for_duplicate: Property.Checkbox({
159
+ displayName: 'Avoid Duplicates?',
160
+ description:
161
+ 'Enable this option to check for duplicate values before inserting data into the sheet. Only unique rows will be added based on the selected column.',
162
+ required: false,
163
+ defaultValue: false,
164
+ }),
165
+ check_for_duplicate_column: Property.DynamicProperties({
166
+ auth: googleSheetsAuth,
167
+ displayName: 'Duplicate Value Column',
168
+ description: 'The column to check for duplicate values.',
169
+ refreshers: ['spreadsheetId', 'sheetId', 'check_for_duplicate', 'headerRow'],
170
+ required: false,
171
+ props: async ({ auth, spreadsheetId, sheetId, check_for_duplicate, headerRow }) => {
172
+ const sheet_id = Number(sheetId);
173
+ const spreadsheet_id = spreadsheetId as unknown as string;
174
+ const checkForExisting = check_for_duplicate as unknown as boolean;
175
+ if (
176
+ !auth ||
177
+ (spreadsheet_id ?? '').toString().length === 0 ||
178
+ (sheet_id ?? '').toString().length === 0
179
+ ) {
180
+ return {};
181
+ }
182
+
183
+ const fields: DynamicPropsValue = {};
184
+
185
+ if (checkForExisting) {
186
+ const headers = await googleSheetsCommon.getGoogleSheetRows({
187
+ spreadsheetId: spreadsheet_id,
188
+ auth: auth as GoogleSheetsAuthValue,
189
+ sheetId: sheet_id,
190
+ rowIndex_s: 1,
191
+ rowIndex_e: 1,
192
+ headerRow: (headerRow as unknown as number) || 1,
193
+ });
194
+ const firstRow = headers[0].values ?? {};
195
+
196
+ //check for empty headers
197
+ if (Object.keys(firstRow).length === 0) {
198
+ fields['markdown'] = Property.MarkDown({
199
+ value: `No headers were found in the selected spreadsheet or worksheet. Please ensure that headers are added to the sheet and refresh the page to display the available columns.`,
200
+ variant: MarkdownVariant.INFO,
201
+ });
202
+ } else {
203
+ const headers: DropdownOption<string>[] = [];
204
+ for (const key in firstRow) {
205
+ headers.push({
206
+ label: firstRow[key].toString(),
207
+ value: key.toString(),
208
+ });
209
+ }
210
+
211
+ fields['column_name'] = Property.StaticDropdown({
212
+ displayName: 'Column to Check for Duplicates',
213
+ description:
214
+ 'Select the column to use for detecting duplicate values. Only rows with unique values in this column will be added to the sheet.',
215
+ required: true,
216
+ options: {
217
+ disabled: false,
218
+ options: headers,
219
+ },
220
+ });
221
+ }
222
+ }
223
+
224
+ return fields;
225
+ },
226
+ }),
227
+ as_string: Property.Checkbox({
228
+ displayName: 'As String',
229
+ description:
230
+ 'Inserted values that are dates and formulas will be entered as strings and have no effect',
231
+ required: false,
232
+ }),
233
+ headerRow: Property.Number({
234
+ displayName: 'Header Row Number',
235
+ description: 'Enter the row number where your column headers are located (usually row 1).',
236
+ required: true,
237
+ defaultValue: 1,
238
+ }),
239
+ },
240
+
241
+ async run(context) {
242
+ const {
243
+ spreadsheetId: inputSpreadsheetId,
244
+ sheetId: inputSheetId,
245
+ input_type: valuesInputType,
246
+ overwrite: overwriteValues,
247
+ check_for_duplicate: checkForDuplicateValues,
248
+ values: { values: rowValuesInput },
249
+ as_string: asString,
250
+ headerRow,
251
+ } = context.propsValue;
252
+
253
+ if (!areSheetIdsValid(inputSpreadsheetId, inputSheetId)) {
254
+ throw new Error('Please select a spreadsheet and sheet first.');
255
+ }
256
+
257
+ const sheetId = Number(inputSheetId);
258
+ const spreadsheetId = inputSpreadsheetId as string;
259
+
260
+ const duplicateColumn = context.propsValue.check_for_duplicate_column?.['column_name'];
261
+ const sheetName = await getWorkSheetName(context.auth, spreadsheetId, sheetId);
262
+
263
+ const rowHeaders = await googleSheetsCommon.getGoogleSheetRows({
264
+ spreadsheetId: spreadsheetId,
265
+ auth: context.auth,
266
+ sheetId: sheetId,
267
+ rowIndex_s: 1,
268
+ rowIndex_e: 1,
269
+ headerRow: headerRow,
270
+ });
271
+
272
+ const sheetHeaders = rowHeaders[0]?.values ?? {};
273
+
274
+ const authClient = await createGoogleClient(context.auth);
275
+ const sheets = google.sheets({ version: 'v4', auth: authClient });
276
+
277
+ const formattedValues = await formatInputRows(
278
+ sheets,
279
+ spreadsheetId,
280
+ sheetName,
281
+ valuesInputType,
282
+ rowValuesInput,
283
+ sheetHeaders,
284
+ );
285
+
286
+ const valueInputOption = asString ? ValueInputOption.RAW : ValueInputOption.USER_ENTERED;
287
+
288
+ if (overwriteValues) {
289
+ const sheetGridRange = await getWorkSheetGridSize(context.auth, spreadsheetId, sheetId);
290
+ const existingGridRowCount = sheetGridRange.rowCount ?? 0;
291
+ return handleOverwrite(
292
+ sheets,
293
+ spreadsheetId,
294
+ sheetName,
295
+ formattedValues,
296
+ existingGridRowCount,
297
+ valueInputOption,
298
+ );
299
+ }
300
+
301
+ if (checkForDuplicateValues) {
302
+ const existingSheetValues = await googleSheetsCommon.getGoogleSheetRows({
303
+ spreadsheetId: spreadsheetId,
304
+ auth: context.auth,
305
+ sheetId: sheetId,
306
+ rowIndex_s: 1,
307
+ rowIndex_e: undefined,
308
+ headerRow: headerRow,
309
+ });
310
+ return handleDuplicates(
311
+ sheets,
312
+ spreadsheetId,
313
+ sheetName,
314
+ formattedValues,
315
+ existingSheetValues,
316
+ duplicateColumn,
317
+ valueInputOption,
318
+ );
319
+ }
320
+
321
+ return normalInsert(sheets, spreadsheetId, sheetName, formattedValues, valueInputOption);
322
+ },
323
+ });
324
+
325
+ async function handleOverwrite(
326
+ sheets: sheets_v4.Sheets,
327
+ spreadSheetId: string,
328
+ sheetName: string,
329
+ formattedValues: any[],
330
+ existingGridRowCount: number,
331
+ valueInputOption: ValueInputOption,
332
+ ) {
333
+ const existingRowCount = existingGridRowCount;
334
+ const inputRowCount = formattedValues.length;
335
+
336
+ const updateResponse = await sheets.spreadsheets.values.batchUpdate({
337
+ spreadsheetId: spreadSheetId,
338
+ requestBody: {
339
+ data: [
340
+ {
341
+ range: `${sheetName}!A2:ZZZ${inputRowCount + 1}`,
342
+ majorDimension: Dimension.ROWS,
343
+ values: formattedValues.map((row) => objectToArray(row)),
344
+ },
345
+ ],
346
+ valueInputOption,
347
+ },
348
+ });
349
+
350
+ // Determine if clearing rows is necessary and within grid size
351
+ const clearStartRow = inputRowCount + 2; // Start clearing after the last input row
352
+ const clearEndRow = Math.max(clearStartRow, existingRowCount);
353
+
354
+ if (clearStartRow <= existingGridRowCount) {
355
+ const boundedClearEndRow = Math.min(clearEndRow, existingGridRowCount);
356
+ const clearRowsResponse = await sheets.spreadsheets.values.batchClear({
357
+ spreadsheetId: spreadSheetId,
358
+ requestBody: {
359
+ ranges: [`${sheetName}!A${clearStartRow}:ZZZ${boundedClearEndRow}`],
360
+ },
361
+ });
362
+
363
+ return {
364
+ ...updateResponse.data,
365
+ ...clearRowsResponse.data,
366
+ };
367
+ }
368
+ return updateResponse.data;
369
+ }
370
+
371
+ async function handleDuplicates(
372
+ sheets: sheets_v4.Sheets,
373
+ spreadSheetId: string,
374
+ sheetName: string,
375
+ formattedInputRows: any[],
376
+ existingSheetValues: any[],
377
+ duplicateColumn: string,
378
+ valueInputOption: ValueInputOption,
379
+ ) {
380
+ const uniqueValues = formattedInputRows.filter(
381
+ (inputRow) =>
382
+ !existingSheetValues.some((existingRow) => {
383
+ const existingValue = existingRow?.values?.[duplicateColumn];
384
+ const inputValue = inputRow?.[duplicateColumn];
385
+ return (
386
+ existingValue != null &&
387
+ inputValue != null &&
388
+ String(existingValue).toLowerCase().trim() === String(inputValue).toLowerCase().trim()
389
+ );
390
+ }),
391
+ );
392
+
393
+ const response = await sheets.spreadsheets.values.append({
394
+ range: sheetName + '!A:A',
395
+ spreadsheetId: spreadSheetId,
396
+ valueInputOption,
397
+ requestBody: {
398
+ values: uniqueValues.map((row) => objectToArray(row)),
399
+ majorDimension: Dimension.ROWS,
400
+ },
401
+ });
402
+
403
+ return response.data;
404
+ }
405
+
406
+ async function normalInsert(
407
+ sheets: sheets_v4.Sheets,
408
+ spreadSheetId: string,
409
+ sheetName: string,
410
+ formattedValues: any[],
411
+ valueInputOption: ValueInputOption,
412
+ ) {
413
+ const response = await sheets.spreadsheets.values.append({
414
+ range: sheetName + '!A:A',
415
+ spreadsheetId: spreadSheetId,
416
+ valueInputOption,
417
+ requestBody: {
418
+ values: formattedValues.map((row) => objectToArray(row)),
419
+ majorDimension: Dimension.ROWS,
420
+ },
421
+ });
422
+ return response.data;
423
+ }
424
+
425
+ async function formatInputRows(
426
+ sheets: sheets_v4.Sheets,
427
+ spreadSheetId: string,
428
+ sheetName: string,
429
+ valuesInputType: string,
430
+ rowValuesInput: any,
431
+ sheetHeaders: RowValueType,
432
+ ): Promise<RowValueType[]> {
433
+ let formattedInputRows: any[] = [];
434
+
435
+ switch (valuesInputType) {
436
+ case 'csv':
437
+ formattedInputRows = convertCsvToRawValues(rowValuesInput as string, ',', sheetHeaders);
438
+ break;
439
+ case 'json':
440
+ formattedInputRows = await convertJsonToRawValues(
441
+ sheets,
442
+ spreadSheetId,
443
+ sheetName,
444
+ rowValuesInput as string,
445
+ sheetHeaders,
446
+ );
447
+ break;
448
+ case 'column_names':
449
+ formattedInputRows = rowValuesInput as RowValueType[];
450
+ break;
451
+ }
452
+
453
+ return formattedInputRows;
454
+ }
455
+
456
+ async function convertJsonToRawValues(
457
+ sheets: sheets_v4.Sheets,
458
+ spreadSheetId: string,
459
+ sheetName: string,
460
+ json: string | Record<string, any>[],
461
+ labelHeaders: RowValueType,
462
+ ): Promise<RowValueType[]> {
463
+ let data: RowValueType[];
464
+
465
+ // If the input is a JSON string
466
+ if (typeof json === 'string') {
467
+ try {
468
+ data = JSON.parse(json);
469
+ } catch (error) {
470
+ throw new Error('Invalid JSON format for row values');
471
+ }
472
+ } else {
473
+ // If the input is already an array of objects, use it directly
474
+ data = json;
475
+ }
476
+
477
+ // Ensure the input is an array of objects
478
+ if (!Array.isArray(data) || typeof data[0] !== 'object') {
479
+ throw new Error('Input must be an array of objects or a valid JSON string representing it.');
480
+ }
481
+
482
+ // Collect all possible headers from the data
483
+ const allHeaders = new Set<string>();
484
+ data.forEach((row) => {
485
+ Object.keys(row).forEach((key) => allHeaders.add(key));
486
+ });
487
+
488
+ // Identify headers not present in labelHeaders
489
+ const additionalHeaders = Array.from(allHeaders).filter(
490
+ (header) => !Object.values(labelHeaders).includes(header),
491
+ );
492
+
493
+ //add missing headers to labelHeaders
494
+ additionalHeaders.forEach((header) => {
495
+ labelHeaders[columnToLabel(Object.keys(labelHeaders).length)] = header;
496
+ });
497
+
498
+ // update sheets with new headers
499
+ if (additionalHeaders.length > 0) {
500
+ await sheets.spreadsheets.values.update({
501
+ range: `${sheetName}!A1:ZZZ1`,
502
+ spreadsheetId: spreadSheetId,
503
+ valueInputOption: ValueInputOption.USER_ENTERED,
504
+ requestBody: {
505
+ majorDimension: Dimension.ROWS,
506
+ values: [objectToArray(labelHeaders)],
507
+ },
508
+ });
509
+ }
510
+
511
+ return data.map((row: RowValueType) => {
512
+ return Object.entries(labelHeaders).reduce((acc, [labelColumn, csvHeader]) => {
513
+ acc[labelColumn] = row[csvHeader] ?? '';
514
+ return acc;
515
+ }, {} as RowValueType);
516
+ });
517
+ }
518
+
519
+ function convertCsvToRawValues(csvText: string, delimiter: string, labelHeaders: RowValueType) {
520
+ // Split CSV into rows
521
+ const rows: Record<string, any>[] = parse(csvText, {
522
+ delimiter: delimiter,
523
+ columns: true,
524
+ });
525
+
526
+ const result = rows.map((row) => {
527
+ // Normalize record keys to lowercase
528
+ const normalizedRow = Object.keys(row).reduce((acc, key) => {
529
+ acc[key.toLowerCase().trim()] = row[key];
530
+ return acc;
531
+ }, {} as Record<string, any>);
532
+
533
+ const transformedRow: Record<string, any> = {};
534
+ for (const key in labelHeaders) {
535
+ // Match labels to normalized keys
536
+ const normalizedKey = labelHeaders[key].toLowerCase();
537
+ transformedRow[key] = normalizedRow[normalizedKey] || '';
538
+ }
539
+ return transformedRow;
540
+ });
541
+ return result;
542
+ }
@@ -0,0 +1,111 @@
1
+ import { createAction, Property } from '@guayaba/workflows-framework';
2
+ import {
3
+ areSheetIdsValid,
4
+ Dimension,
5
+ getAccessToken,
6
+ GoogleSheetsAuthValue,
7
+ googleSheetsCommon,
8
+ objectToArray,
9
+ stringifyArray,
10
+ ValueInputOption,
11
+ } from '../common/common';
12
+ import { googleSheetsAuth } from '../common/common';
13
+ import { isNil } from '@guayaba/workflows-shared';
14
+ import {
15
+ AuthenticationType,
16
+ httpClient,
17
+ HttpMethod,
18
+ HttpRequest,
19
+ } from '@guayaba/workflows-common';
20
+ import { commonProps, isFirstRowHeaderProp, rowValuesProp } from '../common/props';
21
+
22
+ export const insertRowAction = createAction({
23
+ auth: googleSheetsAuth,
24
+ name: 'insert_row',
25
+ description: 'Add a new row of data to a specific spreadsheet.',
26
+ displayName: 'Add Row',
27
+ props: {
28
+ ...commonProps,
29
+ first_row_headers: isFirstRowHeaderProp(),
30
+ as_string: Property.Checkbox({
31
+ displayName: 'As String',
32
+ description:
33
+ 'Inserted values that are dates and formulas will be entered strings and have no effect',
34
+ required: false,
35
+ }),
36
+ values: rowValuesProp(),
37
+ },
38
+ async run({ propsValue, auth }) {
39
+ const {
40
+ values,
41
+ spreadsheetId: inputSpreadsheetId,
42
+ sheetId: inputSheetId,
43
+ as_string,
44
+ first_row_headers,
45
+ } = propsValue;
46
+
47
+ if (!areSheetIdsValid(inputSpreadsheetId, inputSheetId)) {
48
+ throw new Error('Please select a spreadsheet and sheet first.');
49
+ }
50
+
51
+ const sheetId = Number(inputSheetId);
52
+ const spreadsheetId = inputSpreadsheetId as string;
53
+
54
+ const sheetName = await googleSheetsCommon.findSheetName(auth, spreadsheetId, sheetId);
55
+
56
+ const formattedValues = first_row_headers
57
+ ? objectToArray(values).map((val) => (isNil(val) ? '' : val))
58
+ : values.values;
59
+
60
+ const res = await appendGoogleSheetValues({
61
+ auth,
62
+ majorDimension: Dimension.COLUMNS,
63
+ range: sheetName,
64
+ spreadSheetId: spreadsheetId,
65
+ valueInputOption: as_string ? ValueInputOption.RAW : ValueInputOption.USER_ENTERED,
66
+ values: stringifyArray(formattedValues),
67
+ });
68
+
69
+ const updatedRowNumber = extractRowNumber(res.body.updates.updatedRange);
70
+ return { ...res.body, row: updatedRowNumber };
71
+ },
72
+ });
73
+
74
+ function extractRowNumber(updatedRange: string): number {
75
+ const rowRange = updatedRange.split('!')[1];
76
+ return parseInt(rowRange.split(':')[0].substring(1), 10);
77
+ }
78
+
79
+ async function appendGoogleSheetValues(params: AppendGoogleSheetValuesParams) {
80
+ const { auth, majorDimension, range, spreadSheetId, valueInputOption, values } = params;
81
+ const accessToken = await getAccessToken(auth);
82
+ const request: HttpRequest = {
83
+ method: HttpMethod.POST,
84
+ url: `https://sheets.googleapis.com/v4/spreadsheets/${spreadSheetId}/values/${encodeURIComponent(
85
+ `${range}!A:A`,
86
+ )}:append`,
87
+ body: {
88
+ majorDimension,
89
+ range: `${range}!A:A`,
90
+ values: values.map((val) => ({ values: val })),
91
+ },
92
+ authentication: {
93
+ type: AuthenticationType.BEARER_TOKEN,
94
+ token: accessToken,
95
+ },
96
+ queryParams: {
97
+ valueInputOption,
98
+ },
99
+ };
100
+
101
+ return httpClient.sendRequest(request);
102
+ }
103
+
104
+ type AppendGoogleSheetValuesParams = {
105
+ values: string[];
106
+ spreadSheetId: string;
107
+ range: string;
108
+ valueInputOption: ValueInputOption;
109
+ majorDimension: Dimension;
110
+ auth: GoogleSheetsAuthValue;
111
+ };
@@ -0,0 +1,44 @@
1
+ import { googleSheetsAuth } from '../common/common';
2
+ import { createAction, Property } from '@guayaba/workflows-framework';
3
+ import { includeTeamDrivesProp, sheetIdProp, spreadsheetIdProp } from '../common/props';
4
+ import { google } from 'googleapis';
5
+ import { createGoogleClient } from '../common/common';
6
+
7
+ export const renameWorksheetAction = createAction({
8
+ auth: googleSheetsAuth,
9
+ name: 'rename-worksheet',
10
+ displayName: 'Rename Worksheet',
11
+ description: 'Rename specific worksheet.',
12
+ props: {
13
+ includeTeamDrives: includeTeamDrivesProp(),
14
+ spreadsheetId: spreadsheetIdProp('Spreadsheet', 'The ID of the spreadsheet to use.'),
15
+ sheetId: sheetIdProp('Worksheet', 'The ID of the worksheet to rename.'),
16
+ newName:Property.ShortText({
17
+ displayName:'New Sheet Name',
18
+ required:true
19
+ })
20
+ },
21
+ async run(context) {
22
+ const authClient = await createGoogleClient(context.auth);
23
+ const sheets = google.sheets({ version: 'v4', auth: authClient });
24
+
25
+ const response = await sheets.spreadsheets.batchUpdate({
26
+ spreadsheetId: context.propsValue.spreadsheetId,
27
+ requestBody: {
28
+ requests:[
29
+ {
30
+ updateSheetProperties:{
31
+ properties:{
32
+ sheetId:context.propsValue.sheetId,
33
+ title:context.propsValue.newName,
34
+ },
35
+ fields:'title'
36
+ }
37
+ }
38
+ ]
39
+ },
40
+ });
41
+
42
+ return response.data;
43
+ },
44
+ });