runline 0.12.0 → 0.14.0
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.
- package/dist/plugins/_shared/googleSchemas.js +23 -0
- package/dist/plugins/gmail/src/index.js +168 -184
- package/dist/plugins/googleAppsScript/src/index.js +88 -43
- package/dist/plugins/googleCalendar/src/index.js +307 -250
- package/dist/plugins/googleContacts/src/index.js +134 -145
- package/dist/plugins/googleDocs/src/documents.js +27 -38
- package/dist/plugins/googleDocs/src/formatting.js +49 -47
- package/dist/plugins/googleDocs/src/images.js +21 -26
- package/dist/plugins/googleDocs/src/shared.js +55 -0
- package/dist/plugins/googleDocs/src/structure.js +98 -123
- package/dist/plugins/googleDocs/src/tables.js +92 -154
- package/dist/plugins/googleDocs/src/tabs.js +24 -23
- package/dist/plugins/googleDocs/src/text.js +97 -136
- package/dist/plugins/googleDrive/src/index.js +414 -408
- package/dist/plugins/googleImage/src/index.js +13 -16
- package/dist/plugins/googleSheets/src/index.js +504 -339
- package/dist/plugins/googleSlides/src/index.js +85 -72
- package/dist/plugins/googleTasks/src/index.js +82 -92
- package/dist/plugins/shiftLabs/src/index.js +10 -0
- package/dist/plugins/shiftLabs/src/issue-views.js +179 -0
- package/dist/plugins/shiftLabs/src/issues.js +153 -35
- package/dist/plugins/shiftLabs/src/projects.js +93 -0
- package/dist/plugins/shiftLabs/src/shared.js +26 -11
- package/dist/plugins/shiftLabs/src/transcription.js +203 -0
- package/package.json +1 -1
|
@@ -22,14 +22,57 @@
|
|
|
22
22
|
* sheet.deleteDimension — drop N rows or columns starting at an index
|
|
23
23
|
* sheet.batchUpdate — raw passthrough to spreadsheets:batchUpdate
|
|
24
24
|
*
|
|
25
|
+
* chart.add / chart.update / chart.delete
|
|
26
|
+
* namedRange.add / namedRange.delete
|
|
27
|
+
* protectedRange.add / protectedRange.delete
|
|
28
|
+
* conditionalFormat.add / conditionalFormat.delete
|
|
29
|
+
* dataValidation.set
|
|
30
|
+
*
|
|
25
31
|
* Features: auto-column-add on update/upsert, multi-row
|
|
26
32
|
* batchUpdate, RAW vs USER_ENTERED value input, appending via
|
|
27
33
|
* values:append or PUT at lastRow+1, keepFirstRow on clear.
|
|
28
34
|
* Filtering is intentionally the caller's job — `read` returns
|
|
29
35
|
* raw rows / objects and callers post-filter.
|
|
30
36
|
*/
|
|
37
|
+
import * as t from "typebox";
|
|
31
38
|
import { googleAccessToken } from "../../_shared/googleAuth.js";
|
|
39
|
+
import { Id, JsonValue, NonEmptyString, NonNegativeInteger, PositiveInteger, RawGoogleObject, StringArray, stringEnum, } from "../../_shared/googleSchemas.js";
|
|
32
40
|
const ROW_NUMBER = "row_number";
|
|
41
|
+
const sheetReferenceSchema = t.Union([NonEmptyString, NonNegativeInteger]);
|
|
42
|
+
const cellValueSchema = t.Union([
|
|
43
|
+
t.String(),
|
|
44
|
+
t.Number(),
|
|
45
|
+
t.Boolean(),
|
|
46
|
+
t.Null(),
|
|
47
|
+
]);
|
|
48
|
+
const objectRowSchema = t.Record(t.String(), JsonValue);
|
|
49
|
+
const rowsSchema = t.Union([
|
|
50
|
+
t.Array(t.Array(cellValueSchema)),
|
|
51
|
+
t.Array(objectRowSchema),
|
|
52
|
+
]);
|
|
53
|
+
const valueInputOptionSchema = stringEnum(["RAW", "USER_ENTERED"]);
|
|
54
|
+
const handlingExtraDataSchema = stringEnum([
|
|
55
|
+
"ignore",
|
|
56
|
+
"error",
|
|
57
|
+
"insertInNewColumn",
|
|
58
|
+
]);
|
|
59
|
+
const initialSheetSchema = t.Object({
|
|
60
|
+
title: NonEmptyString,
|
|
61
|
+
sheetId: t.Optional(NonNegativeInteger),
|
|
62
|
+
index: t.Optional(NonNegativeInteger),
|
|
63
|
+
sheetType: t.Optional(stringEnum(["GRID", "OBJECT", "DATA_SOURCE"])),
|
|
64
|
+
hidden: t.Optional(t.Boolean()),
|
|
65
|
+
rightToLeft: t.Optional(t.Boolean()),
|
|
66
|
+
gridProperties: t.Optional(t.Object({
|
|
67
|
+
rowCount: t.Optional(PositiveInteger),
|
|
68
|
+
columnCount: t.Optional(PositiveInteger),
|
|
69
|
+
frozenRowCount: t.Optional(NonNegativeInteger),
|
|
70
|
+
frozenColumnCount: t.Optional(NonNegativeInteger),
|
|
71
|
+
hideGridlines: t.Optional(t.Boolean()),
|
|
72
|
+
rowGroupControlAfter: t.Optional(t.Boolean()),
|
|
73
|
+
columnGroupControlAfter: t.Optional(t.Boolean()),
|
|
74
|
+
}, { additionalProperties: false })),
|
|
75
|
+
}, { additionalProperties: false });
|
|
33
76
|
// ─── Auth ────────────────────────────────────────────────────────
|
|
34
77
|
async function accessToken(ctx) {
|
|
35
78
|
return googleAccessToken(ctx, "googleSheets", SCOPES);
|
|
@@ -60,7 +103,8 @@ async function sheetsRequest(ctx, method, path, body, qs, baseOverride) {
|
|
|
60
103
|
},
|
|
61
104
|
};
|
|
62
105
|
if (body && Object.keys(body).length > 0) {
|
|
63
|
-
init.headers["Content-Type"] =
|
|
106
|
+
init.headers["Content-Type"] =
|
|
107
|
+
"application/json";
|
|
64
108
|
init.body = JSON.stringify(body);
|
|
65
109
|
}
|
|
66
110
|
const res = await fetch(url.toString(), init);
|
|
@@ -322,7 +366,13 @@ function a1RangeToGridRange(a1, sheetId) {
|
|
|
322
366
|
const startRow = parseInt(m[2], 10) - 1;
|
|
323
367
|
const endCol = columnLetterToNumber(m[3]);
|
|
324
368
|
const endRow = parseInt(m[4], 10);
|
|
325
|
-
return {
|
|
369
|
+
return {
|
|
370
|
+
sheetId,
|
|
371
|
+
startRowIndex: startRow,
|
|
372
|
+
endRowIndex: endRow,
|
|
373
|
+
startColumnIndex: startCol,
|
|
374
|
+
endColumnIndex: endCol,
|
|
375
|
+
};
|
|
326
376
|
}
|
|
327
377
|
export default function googleSheets(rl) {
|
|
328
378
|
rl.setName("googleSheets");
|
|
@@ -410,20 +460,12 @@ export default function googleSheets(rl) {
|
|
|
410
460
|
rl.registerAction("spreadsheet.create", {
|
|
411
461
|
access: "write",
|
|
412
462
|
description: "Create a new spreadsheet",
|
|
413
|
-
inputSchema: {
|
|
414
|
-
title:
|
|
415
|
-
sheets:
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
},
|
|
420
|
-
locale: { type: "string", required: false, description: "e.g. en_US" },
|
|
421
|
-
autoRecalc: {
|
|
422
|
-
type: "string",
|
|
423
|
-
required: false,
|
|
424
|
-
description: "ON_CHANGE | MINUTE | HOUR",
|
|
425
|
-
},
|
|
426
|
-
},
|
|
463
|
+
inputSchema: t.Object({
|
|
464
|
+
title: NonEmptyString,
|
|
465
|
+
sheets: t.Optional(t.Array(initialSheetSchema)),
|
|
466
|
+
locale: t.Optional(NonEmptyString),
|
|
467
|
+
autoRecalc: t.Optional(stringEnum(["ON_CHANGE", "MINUTE", "HOUR"])),
|
|
468
|
+
}, { additionalProperties: false }),
|
|
427
469
|
async execute(input, ctx) {
|
|
428
470
|
const p = (input ?? {});
|
|
429
471
|
const body = {
|
|
@@ -444,16 +486,12 @@ export default function googleSheets(rl) {
|
|
|
444
486
|
rl.registerAction("spreadsheet.get", {
|
|
445
487
|
access: "read",
|
|
446
488
|
description: "Get spreadsheet metadata (sheets, properties, optional grid data)",
|
|
447
|
-
inputSchema: {
|
|
448
|
-
spreadsheetId:
|
|
449
|
-
includeGridData:
|
|
450
|
-
ranges:
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
description: "Limit the response to specific A1 ranges",
|
|
454
|
-
},
|
|
455
|
-
fields: { type: "string", required: false },
|
|
456
|
-
},
|
|
489
|
+
inputSchema: t.Object({
|
|
490
|
+
spreadsheetId: Id,
|
|
491
|
+
includeGridData: t.Optional(t.Boolean()),
|
|
492
|
+
ranges: t.Optional(StringArray),
|
|
493
|
+
fields: t.Optional(NonEmptyString),
|
|
494
|
+
}, { additionalProperties: false }),
|
|
457
495
|
async execute(input, ctx) {
|
|
458
496
|
const p = (input ?? {});
|
|
459
497
|
const qs = {};
|
|
@@ -469,7 +507,7 @@ export default function googleSheets(rl) {
|
|
|
469
507
|
rl.registerAction("spreadsheet.delete", {
|
|
470
508
|
access: "write",
|
|
471
509
|
description: "Delete a spreadsheet (via Drive API — requires drive.file scope)",
|
|
472
|
-
inputSchema: { spreadsheetId:
|
|
510
|
+
inputSchema: t.Object({ spreadsheetId: Id }, { additionalProperties: false }),
|
|
473
511
|
async execute(input, ctx) {
|
|
474
512
|
const p = (input ?? {});
|
|
475
513
|
await sheetsRequest(ctx, "DELETE", `/drive/v3/files/${p.spreadsheetId}`, undefined, undefined, "https://www.googleapis.com");
|
|
@@ -480,7 +518,7 @@ export default function googleSheets(rl) {
|
|
|
480
518
|
rl.registerAction("sheet.listTabs", {
|
|
481
519
|
access: "read",
|
|
482
520
|
description: "List sheet tabs in a spreadsheet",
|
|
483
|
-
inputSchema: { spreadsheetId:
|
|
521
|
+
inputSchema: t.Object({ spreadsheetId: Id }, { additionalProperties: false }),
|
|
484
522
|
async execute(input, ctx) {
|
|
485
523
|
const p = (input ?? {});
|
|
486
524
|
return getSheetProperties(ctx, p.spreadsheetId);
|
|
@@ -489,15 +527,15 @@ export default function googleSheets(rl) {
|
|
|
489
527
|
rl.registerAction("sheet.addTab", {
|
|
490
528
|
access: "write",
|
|
491
529
|
description: "Add a new sheet tab",
|
|
492
|
-
inputSchema: {
|
|
493
|
-
spreadsheetId:
|
|
494
|
-
title:
|
|
495
|
-
index:
|
|
496
|
-
sheetId:
|
|
497
|
-
hidden:
|
|
498
|
-
rightToLeft:
|
|
499
|
-
tabColor: {
|
|
500
|
-
},
|
|
530
|
+
inputSchema: t.Object({
|
|
531
|
+
spreadsheetId: Id,
|
|
532
|
+
title: NonEmptyString,
|
|
533
|
+
index: t.Optional(NonNegativeInteger),
|
|
534
|
+
sheetId: t.Optional(NonNegativeInteger),
|
|
535
|
+
hidden: t.Optional(t.Boolean()),
|
|
536
|
+
rightToLeft: t.Optional(t.Boolean()),
|
|
537
|
+
tabColor: t.Optional(t.String({ pattern: "^#?[0-9a-fA-F]{6}$" })),
|
|
538
|
+
}, { additionalProperties: false }),
|
|
501
539
|
async execute(input, ctx) {
|
|
502
540
|
const p = (input ?? {});
|
|
503
541
|
const properties = { title: p.title };
|
|
@@ -521,14 +559,10 @@ export default function googleSheets(rl) {
|
|
|
521
559
|
rl.registerAction("sheet.deleteTab", {
|
|
522
560
|
access: "write",
|
|
523
561
|
description: "Delete a sheet tab",
|
|
524
|
-
inputSchema: {
|
|
525
|
-
spreadsheetId:
|
|
526
|
-
sheet:
|
|
527
|
-
|
|
528
|
-
required: true,
|
|
529
|
-
description: "Tab title or numeric sheetId",
|
|
530
|
-
},
|
|
531
|
-
},
|
|
562
|
+
inputSchema: t.Object({
|
|
563
|
+
spreadsheetId: Id,
|
|
564
|
+
sheet: sheetReferenceSchema,
|
|
565
|
+
}, { additionalProperties: false }),
|
|
532
566
|
async execute(input, ctx) {
|
|
533
567
|
const p = (input ?? {});
|
|
534
568
|
const sheetId = await resolveSheetId(ctx, p.spreadsheetId, p.sheet);
|
|
@@ -542,40 +576,20 @@ export default function googleSheets(rl) {
|
|
|
542
576
|
rl.registerAction("sheet.read", {
|
|
543
577
|
access: "read",
|
|
544
578
|
description: "Read values from a range. Returns a 2D array by default; set `asObjects=true` to pair rows with a header row and return objects.",
|
|
545
|
-
inputSchema: {
|
|
546
|
-
spreadsheetId:
|
|
547
|
-
range:
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
type: "number",
|
|
560
|
-
required: false,
|
|
561
|
-
description: "1-indexed first data row (default: headerRow + 1)",
|
|
562
|
-
},
|
|
563
|
-
valueRenderOption: {
|
|
564
|
-
type: "string",
|
|
565
|
-
required: false,
|
|
566
|
-
description: "FORMATTED_VALUE (default) | UNFORMATTED_VALUE | FORMULA",
|
|
567
|
-
},
|
|
568
|
-
dateTimeRenderOption: {
|
|
569
|
-
type: "string",
|
|
570
|
-
required: false,
|
|
571
|
-
description: "FORMATTED_STRING (default) | SERIAL_NUMBER",
|
|
572
|
-
},
|
|
573
|
-
includeRowNumber: {
|
|
574
|
-
type: "boolean",
|
|
575
|
-
required: false,
|
|
576
|
-
description: "Attach `row_number` to each object (asObjects only)",
|
|
577
|
-
},
|
|
578
|
-
},
|
|
579
|
+
inputSchema: t.Object({
|
|
580
|
+
spreadsheetId: Id,
|
|
581
|
+
range: NonEmptyString,
|
|
582
|
+
asObjects: t.Optional(t.Boolean()),
|
|
583
|
+
headerRow: t.Optional(PositiveInteger),
|
|
584
|
+
dataStartRow: t.Optional(PositiveInteger),
|
|
585
|
+
valueRenderOption: t.Optional(stringEnum([
|
|
586
|
+
"FORMATTED_VALUE",
|
|
587
|
+
"UNFORMATTED_VALUE",
|
|
588
|
+
"FORMULA",
|
|
589
|
+
])),
|
|
590
|
+
dateTimeRenderOption: t.Optional(stringEnum(["FORMATTED_STRING", "SERIAL_NUMBER"])),
|
|
591
|
+
includeRowNumber: t.Optional(t.Boolean()),
|
|
592
|
+
}, { additionalProperties: false }),
|
|
579
593
|
async execute(input, ctx) {
|
|
580
594
|
const p = (input ?? {});
|
|
581
595
|
const rows = await getValues(ctx, p.spreadsheetId, p.range, p.valueRenderOption ?? "FORMATTED_VALUE", p.dateTimeRenderOption ?? "FORMATTED_STRING");
|
|
@@ -599,39 +613,15 @@ export default function googleSheets(rl) {
|
|
|
599
613
|
rl.registerAction("sheet.append", {
|
|
600
614
|
access: "write",
|
|
601
615
|
description: "Append rows to the bottom. Pass `rows` as arrays (matching columns) or objects (keyed by header names; extra keys become new columns by default).",
|
|
602
|
-
inputSchema: {
|
|
603
|
-
spreadsheetId:
|
|
604
|
-
sheet:
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
required: true,
|
|
612
|
-
description: "Array of arrays, or array of objects",
|
|
613
|
-
},
|
|
614
|
-
headerRow: {
|
|
615
|
-
type: "number",
|
|
616
|
-
required: false,
|
|
617
|
-
description: "1-indexed header row (default: 1, used when rows are objects)",
|
|
618
|
-
},
|
|
619
|
-
valueInputOption: {
|
|
620
|
-
type: "string",
|
|
621
|
-
required: false,
|
|
622
|
-
description: "USER_ENTERED (default) | RAW",
|
|
623
|
-
},
|
|
624
|
-
handlingExtraData: {
|
|
625
|
-
type: "string",
|
|
626
|
-
required: false,
|
|
627
|
-
description: "insertInNewColumn (default) | ignore | error",
|
|
628
|
-
},
|
|
629
|
-
useAppend: {
|
|
630
|
-
type: "boolean",
|
|
631
|
-
required: false,
|
|
632
|
-
description: "Use values:append (safer w/ formulas but rewrites filters) instead of PUT at lastRow+1",
|
|
633
|
-
},
|
|
634
|
-
},
|
|
616
|
+
inputSchema: t.Object({
|
|
617
|
+
spreadsheetId: Id,
|
|
618
|
+
sheet: sheetReferenceSchema,
|
|
619
|
+
rows: rowsSchema,
|
|
620
|
+
headerRow: t.Optional(PositiveInteger),
|
|
621
|
+
valueInputOption: t.Optional(valueInputOptionSchema),
|
|
622
|
+
handlingExtraData: t.Optional(handlingExtraDataSchema),
|
|
623
|
+
useAppend: t.Optional(t.Boolean()),
|
|
624
|
+
}, { additionalProperties: false }),
|
|
635
625
|
async execute(input, ctx) {
|
|
636
626
|
const p = (input ?? {});
|
|
637
627
|
const spreadsheetId = p.spreadsheetId;
|
|
@@ -694,20 +684,16 @@ export default function googleSheets(rl) {
|
|
|
694
684
|
rl.registerAction("sheet.update", {
|
|
695
685
|
access: "write",
|
|
696
686
|
description: "Update rows matched by a key column (or by the synthetic 'row_number' field). Rows are objects; undefined/null values are skipped.",
|
|
697
|
-
inputSchema: {
|
|
698
|
-
spreadsheetId:
|
|
699
|
-
sheet:
|
|
700
|
-
rows:
|
|
701
|
-
matchKey:
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
dataStartRow: { type: "number", required: false },
|
|
708
|
-
valueInputOption: { type: "string", required: false },
|
|
709
|
-
handlingExtraData: { type: "string", required: false },
|
|
710
|
-
},
|
|
687
|
+
inputSchema: t.Object({
|
|
688
|
+
spreadsheetId: Id,
|
|
689
|
+
sheet: sheetReferenceSchema,
|
|
690
|
+
rows: t.Array(objectRowSchema),
|
|
691
|
+
matchKey: NonEmptyString,
|
|
692
|
+
headerRow: t.Optional(PositiveInteger),
|
|
693
|
+
dataStartRow: t.Optional(PositiveInteger),
|
|
694
|
+
valueInputOption: t.Optional(valueInputOptionSchema),
|
|
695
|
+
handlingExtraData: t.Optional(handlingExtraDataSchema),
|
|
696
|
+
}, { additionalProperties: false }),
|
|
711
697
|
async execute(input, ctx) {
|
|
712
698
|
const p = (input ?? {});
|
|
713
699
|
return runUpdateOrUpsert(ctx, p, false);
|
|
@@ -716,17 +702,17 @@ export default function googleSheets(rl) {
|
|
|
716
702
|
rl.registerAction("sheet.appendOrUpdate", {
|
|
717
703
|
access: "write",
|
|
718
704
|
description: "Upsert rows. Rows whose match value is found are updated in place; rows with a missing or unknown match value are appended.",
|
|
719
|
-
inputSchema: {
|
|
720
|
-
spreadsheetId:
|
|
721
|
-
sheet:
|
|
722
|
-
rows:
|
|
723
|
-
matchKey:
|
|
724
|
-
headerRow:
|
|
725
|
-
dataStartRow:
|
|
726
|
-
valueInputOption:
|
|
727
|
-
handlingExtraData:
|
|
728
|
-
useAppend:
|
|
729
|
-
},
|
|
705
|
+
inputSchema: t.Object({
|
|
706
|
+
spreadsheetId: Id,
|
|
707
|
+
sheet: sheetReferenceSchema,
|
|
708
|
+
rows: t.Array(objectRowSchema),
|
|
709
|
+
matchKey: NonEmptyString,
|
|
710
|
+
headerRow: t.Optional(PositiveInteger),
|
|
711
|
+
dataStartRow: t.Optional(PositiveInteger),
|
|
712
|
+
valueInputOption: t.Optional(valueInputOptionSchema),
|
|
713
|
+
handlingExtraData: t.Optional(handlingExtraDataSchema),
|
|
714
|
+
useAppend: t.Optional(t.Boolean()),
|
|
715
|
+
}, { additionalProperties: false }),
|
|
730
716
|
async execute(input, ctx) {
|
|
731
717
|
const p = (input ?? {});
|
|
732
718
|
return runUpdateOrUpsert(ctx, p, true);
|
|
@@ -736,25 +722,70 @@ export default function googleSheets(rl) {
|
|
|
736
722
|
rl.registerAction("sheet.clear", {
|
|
737
723
|
access: "write",
|
|
738
724
|
description: "Clear values from a sheet. Modes: wholeSheet (optional keepFirstRow) | rows | columns | range.",
|
|
739
|
-
inputSchema: {
|
|
740
|
-
spreadsheetId:
|
|
741
|
-
sheet:
|
|
742
|
-
mode:
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
725
|
+
inputSchema: t.Object({
|
|
726
|
+
spreadsheetId: Id,
|
|
727
|
+
sheet: sheetReferenceSchema,
|
|
728
|
+
mode: t.Optional(stringEnum(["wholeSheet", "rows", "columns", "range"])),
|
|
729
|
+
startRow: t.Optional(PositiveInteger),
|
|
730
|
+
rowCount: t.Optional(PositiveInteger),
|
|
731
|
+
startColumn: t.Optional(t.String({ pattern: "^[A-Za-z]+$" })),
|
|
732
|
+
columnCount: t.Optional(PositiveInteger),
|
|
733
|
+
range: t.Optional(NonEmptyString),
|
|
734
|
+
keepFirstRow: t.Optional(t.Boolean()),
|
|
735
|
+
}, {
|
|
736
|
+
additionalProperties: false,
|
|
737
|
+
oneOf: [
|
|
738
|
+
{
|
|
739
|
+
properties: { mode: { const: "wholeSheet" } },
|
|
740
|
+
not: {
|
|
741
|
+
anyOf: [
|
|
742
|
+
{ required: ["startRow"] },
|
|
743
|
+
{ required: ["rowCount"] },
|
|
744
|
+
{ required: ["startColumn"] },
|
|
745
|
+
{ required: ["columnCount"] },
|
|
746
|
+
{ required: ["range"] },
|
|
747
|
+
],
|
|
748
|
+
},
|
|
749
|
+
},
|
|
750
|
+
{
|
|
751
|
+
properties: { mode: { const: "rows" } },
|
|
752
|
+
required: ["mode"],
|
|
753
|
+
not: {
|
|
754
|
+
anyOf: [
|
|
755
|
+
{ required: ["startColumn"] },
|
|
756
|
+
{ required: ["columnCount"] },
|
|
757
|
+
{ required: ["range"] },
|
|
758
|
+
{ required: ["keepFirstRow"] },
|
|
759
|
+
],
|
|
760
|
+
},
|
|
761
|
+
},
|
|
762
|
+
{
|
|
763
|
+
properties: { mode: { const: "columns" } },
|
|
764
|
+
required: ["mode"],
|
|
765
|
+
not: {
|
|
766
|
+
anyOf: [
|
|
767
|
+
{ required: ["startRow"] },
|
|
768
|
+
{ required: ["rowCount"] },
|
|
769
|
+
{ required: ["range"] },
|
|
770
|
+
{ required: ["keepFirstRow"] },
|
|
771
|
+
],
|
|
772
|
+
},
|
|
773
|
+
},
|
|
774
|
+
{
|
|
775
|
+
properties: { mode: { const: "range" } },
|
|
776
|
+
required: ["mode", "range"],
|
|
777
|
+
not: {
|
|
778
|
+
anyOf: [
|
|
779
|
+
{ required: ["startRow"] },
|
|
780
|
+
{ required: ["rowCount"] },
|
|
781
|
+
{ required: ["startColumn"] },
|
|
782
|
+
{ required: ["columnCount"] },
|
|
783
|
+
{ required: ["keepFirstRow"] },
|
|
784
|
+
],
|
|
785
|
+
},
|
|
786
|
+
},
|
|
787
|
+
],
|
|
788
|
+
}),
|
|
758
789
|
async execute(input, ctx) {
|
|
759
790
|
const p = (input ?? {});
|
|
760
791
|
const sheetName = typeof p.sheet === "number"
|
|
@@ -779,7 +810,9 @@ export default function googleSheets(rl) {
|
|
|
779
810
|
}
|
|
780
811
|
else if (mode === "range") {
|
|
781
812
|
const region = String(p.range ?? "");
|
|
782
|
-
range = region.includes("!")
|
|
813
|
+
range = region.includes("!")
|
|
814
|
+
? `${sheetName}!${region.split("!")[1]}`
|
|
815
|
+
: `${sheetName}!${region}`;
|
|
783
816
|
}
|
|
784
817
|
else {
|
|
785
818
|
range = sheetName;
|
|
@@ -799,17 +832,13 @@ export default function googleSheets(rl) {
|
|
|
799
832
|
rl.registerAction("sheet.deleteDimension", {
|
|
800
833
|
access: "write",
|
|
801
834
|
description: "Delete a range of rows or columns",
|
|
802
|
-
inputSchema: {
|
|
803
|
-
spreadsheetId:
|
|
804
|
-
sheet:
|
|
805
|
-
dimension:
|
|
806
|
-
startIndex:
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
description: "1-indexed (row number) or column letter position",
|
|
810
|
-
},
|
|
811
|
-
count: { type: "number", required: false, description: "How many to delete (default: 1)" },
|
|
812
|
-
},
|
|
835
|
+
inputSchema: t.Object({
|
|
836
|
+
spreadsheetId: Id,
|
|
837
|
+
sheet: sheetReferenceSchema,
|
|
838
|
+
dimension: stringEnum(["ROWS", "COLUMNS"]),
|
|
839
|
+
startIndex: PositiveInteger,
|
|
840
|
+
count: t.Optional(PositiveInteger),
|
|
841
|
+
}, { additionalProperties: false }),
|
|
813
842
|
async execute(input, ctx) {
|
|
814
843
|
const p = (input ?? {});
|
|
815
844
|
const sheetId = await resolveSheetId(ctx, p.spreadsheetId, p.sheet);
|
|
@@ -834,114 +863,153 @@ export default function googleSheets(rl) {
|
|
|
834
863
|
rl.registerAction("sheet.batchUpdate", {
|
|
835
864
|
access: "write",
|
|
836
865
|
description: "Raw passthrough to spreadsheets:batchUpdate for anything this plugin doesn't expose directly (formatting, merges, conditional rules, …).",
|
|
837
|
-
inputSchema: {
|
|
838
|
-
spreadsheetId:
|
|
839
|
-
requests: {
|
|
840
|
-
},
|
|
866
|
+
inputSchema: t.Object({
|
|
867
|
+
spreadsheetId: Id,
|
|
868
|
+
requests: t.Array(RawGoogleObject, { minItems: 1 }),
|
|
869
|
+
}, { additionalProperties: false }),
|
|
841
870
|
async execute(input, ctx) {
|
|
842
871
|
const p = (input ?? {});
|
|
843
872
|
return spreadsheetBatchUpdate(ctx, p.spreadsheetId, p.requests);
|
|
844
873
|
},
|
|
845
874
|
});
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
return { updated: 0, appended: 0 };
|
|
860
|
-
}
|
|
861
|
-
const matchKey = p.matchKey;
|
|
862
|
-
if (!matchKey)
|
|
863
|
-
throw new Error("googleSheets: matchKey is required");
|
|
864
|
-
const valueInputOption = p.valueInputOption ?? "USER_ENTERED";
|
|
865
|
-
const allProps = await getSheetProperties(ctx, spreadsheetId);
|
|
866
|
-
const prop = allProps.find((s) => s.title === sheetIdOrName || String(s.sheetId) === String(sheetIdOrName));
|
|
867
|
-
if (!prop)
|
|
868
|
-
throw new Error(`googleSheets: sheet "${sheetIdOrName}" not found`);
|
|
869
|
-
const sheetName = prop.title;
|
|
870
|
-
const sheetId = prop.sheetId;
|
|
871
|
-
const headerRowIdx = Math.max(1, p.headerRow ?? 1) - 1;
|
|
872
|
-
const dataStartRowIdx = Math.max(headerRowIdx + 1, (p.dataStartRow ?? headerRowIdx + 2) - 1);
|
|
873
|
-
const handlingExtraData = p.handlingExtraData ?? "insertInNewColumn";
|
|
874
|
-
// Headers come from FORMATTED_VALUE (cosmetic strings). The match
|
|
875
|
-
// column needs UNFORMATTED_VALUE so "1,234" in input matches 1234
|
|
876
|
-
// on the sheet (issue a second fetch with UNFORMATTED_VALUE).
|
|
877
|
-
const sheetData = await getValues(ctx, spreadsheetId, sheetName, "FORMATTED_VALUE");
|
|
878
|
-
let headers = sheetData[headerRowIdx] ?? [];
|
|
879
|
-
let keyColumnValues = [];
|
|
880
|
-
if (matchKey !== ROW_NUMBER) {
|
|
881
|
-
const idx = headers.indexOf(matchKey);
|
|
882
|
-
if (idx === -1) {
|
|
883
|
-
if (!upsert)
|
|
884
|
-
throw new Error(`googleSheets: match column "${matchKey}" not found`);
|
|
875
|
+
/**
|
|
876
|
+
* Shared implementation for `sheet.update` and `sheet.appendOrUpdate`.
|
|
877
|
+
* Reads current headers + the key column, decides per row whether
|
|
878
|
+
* it's an update or an append, issues one `values:batchUpdate` for
|
|
879
|
+
* the updates, and (in upsert mode) falls through to the append
|
|
880
|
+
* path for the rest.
|
|
881
|
+
*/
|
|
882
|
+
async function runUpdateOrUpsert(ctx, p, upsert) {
|
|
883
|
+
const spreadsheetId = p.spreadsheetId;
|
|
884
|
+
const sheetIdOrName = p.sheet;
|
|
885
|
+
const rows = p.rows;
|
|
886
|
+
if (!Array.isArray(rows) || rows.length === 0) {
|
|
887
|
+
return { updated: 0, appended: 0 };
|
|
885
888
|
}
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
+
const matchKey = p.matchKey;
|
|
890
|
+
if (!matchKey)
|
|
891
|
+
throw new Error("googleSheets: matchKey is required");
|
|
892
|
+
const valueInputOption = p.valueInputOption ?? "USER_ENTERED";
|
|
893
|
+
const allProps = await getSheetProperties(ctx, spreadsheetId);
|
|
894
|
+
const prop = allProps.find((s) => s.title === sheetIdOrName ||
|
|
895
|
+
String(s.sheetId) === String(sheetIdOrName));
|
|
896
|
+
if (!prop)
|
|
897
|
+
throw new Error(`googleSheets: sheet "${sheetIdOrName}" not found`);
|
|
898
|
+
const sheetName = prop.title;
|
|
899
|
+
const sheetId = prop.sheetId;
|
|
900
|
+
const headerRowIdx = Math.max(1, p.headerRow ?? 1) - 1;
|
|
901
|
+
const dataStartRowIdx = Math.max(headerRowIdx + 1, (p.dataStartRow ?? headerRowIdx + 2) - 1);
|
|
902
|
+
const handlingExtraData = p.handlingExtraData ??
|
|
903
|
+
"insertInNewColumn";
|
|
904
|
+
// Headers come from FORMATTED_VALUE (cosmetic strings). The match
|
|
905
|
+
// column needs UNFORMATTED_VALUE so "1,234" in input matches 1234
|
|
906
|
+
// on the sheet (issue a second fetch with UNFORMATTED_VALUE).
|
|
907
|
+
const sheetData = await getValues(ctx, spreadsheetId, sheetName, "FORMATTED_VALUE");
|
|
908
|
+
let headers = sheetData[headerRowIdx] ?? [];
|
|
909
|
+
let keyColumnValues = [];
|
|
910
|
+
if (matchKey !== ROW_NUMBER) {
|
|
911
|
+
const idx = headers.indexOf(matchKey);
|
|
912
|
+
if (idx === -1) {
|
|
913
|
+
if (!upsert)
|
|
914
|
+
throw new Error(`googleSheets: match column "${matchKey}" not found`);
|
|
915
|
+
}
|
|
916
|
+
else {
|
|
917
|
+
const unformatted = await getValues(ctx, spreadsheetId, sheetName, "UNFORMATTED_VALUE");
|
|
918
|
+
keyColumnValues = unformatted
|
|
919
|
+
.slice(dataStartRowIdx)
|
|
920
|
+
.map((r) => String(r[idx] ?? ""));
|
|
921
|
+
}
|
|
889
922
|
}
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
}, { valueInputOption });
|
|
899
|
-
}
|
|
900
|
-
// Qualify update ranges with the sheet name (prepareUpdateOrUpsert
|
|
901
|
-
// returned bare A1 cells like "C5").
|
|
902
|
-
const updateData = prepared.updateData.map((d) => ({
|
|
903
|
-
range: `${sheetName}!${d.range}`,
|
|
904
|
-
values: d.values,
|
|
905
|
-
}));
|
|
906
|
-
if (updateData.length > 0) {
|
|
907
|
-
await batchValuesUpdate(ctx, spreadsheetId, updateData, valueInputOption);
|
|
908
|
-
}
|
|
909
|
-
if (upsert && prepared.appendData.length > 0) {
|
|
910
|
-
const values = objectsToRows(prepared.appendData, headers);
|
|
911
|
-
if (p.useAppend) {
|
|
912
|
-
await sheetsRequest(ctx, "POST", `/v4/spreadsheets/${spreadsheetId}/values/${encodeA1(sheetName)}:append`, { range: sheetName, values }, { valueInputOption, insertDataOption: "INSERT_ROWS" });
|
|
923
|
+
const prepared = prepareUpdateOrUpsert(rows, headers, keyColumnValues, dataStartRowIdx, matchKey, { upsert, handlingExtraData });
|
|
924
|
+
// If we added new columns, extend the header row on the sheet first.
|
|
925
|
+
if (prepared.newColumns.length > 0) {
|
|
926
|
+
headers = [...headers, ...prepared.newColumns];
|
|
927
|
+
await sheetsRequest(ctx, "PUT", `/v4/spreadsheets/${spreadsheetId}/values/${encodeA1(`${sheetName}!${headerRowIdx + 1}:${headerRowIdx + 1}`)}`, {
|
|
928
|
+
range: `${sheetName}!${headerRowIdx + 1}:${headerRowIdx + 1}`,
|
|
929
|
+
values: [headers],
|
|
930
|
+
}, { valueInputOption });
|
|
913
931
|
}
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
932
|
+
// Qualify update ranges with the sheet name (prepareUpdateOrUpsert
|
|
933
|
+
// returned bare A1 cells like "C5").
|
|
934
|
+
const updateData = prepared.updateData.map((d) => ({
|
|
935
|
+
range: `${sheetName}!${d.range}`,
|
|
936
|
+
values: d.values,
|
|
937
|
+
}));
|
|
938
|
+
if (updateData.length > 0) {
|
|
939
|
+
await batchValuesUpdate(ctx, spreadsheetId, updateData, valueInputOption);
|
|
919
940
|
}
|
|
941
|
+
if (upsert && prepared.appendData.length > 0) {
|
|
942
|
+
const values = objectsToRows(prepared.appendData, headers);
|
|
943
|
+
if (p.useAppend) {
|
|
944
|
+
await sheetsRequest(ctx, "POST", `/v4/spreadsheets/${spreadsheetId}/values/${encodeA1(sheetName)}:append`, { range: sheetName, values }, { valueInputOption, insertDataOption: "INSERT_ROWS" });
|
|
945
|
+
}
|
|
946
|
+
else {
|
|
947
|
+
const lastRow = sheetData.length;
|
|
948
|
+
await appendEmptyRow(ctx, spreadsheetId, sheetId);
|
|
949
|
+
const targetRange = `${sheetName}!${lastRow + 1}:${lastRow + values.length}`;
|
|
950
|
+
await sheetsRequest(ctx, "PUT", `/v4/spreadsheets/${spreadsheetId}/values/${encodeA1(targetRange)}`, { range: targetRange, values }, { valueInputOption });
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
return {
|
|
954
|
+
updated: prepared.updateData.length,
|
|
955
|
+
appended: prepared.appendData.length,
|
|
956
|
+
newColumns: prepared.newColumns,
|
|
957
|
+
};
|
|
920
958
|
}
|
|
921
|
-
return {
|
|
922
|
-
updated: prepared.updateData.length,
|
|
923
|
-
appended: prepared.appendData.length,
|
|
924
|
-
newColumns: prepared.newColumns,
|
|
925
|
-
};
|
|
926
959
|
// ─── Charts ─────────────────────────────────────────────────────
|
|
927
960
|
rl.registerAction("chart.add", {
|
|
928
961
|
access: "write",
|
|
929
962
|
description: "Embed a chart on a sheet. Minimal signature: type ('COLUMN'|'BAR'|'LINE'|'AREA'|'PIE'|'SCATTER'|'COMBO'), data source (sheet name + A1 range), and target anchor cell. For full control pass a raw `chartSpec` object instead.",
|
|
930
|
-
inputSchema: {
|
|
931
|
-
spreadsheetId:
|
|
932
|
-
title:
|
|
933
|
-
type:
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
963
|
+
inputSchema: t.Object({
|
|
964
|
+
spreadsheetId: Id,
|
|
965
|
+
title: t.Optional(t.String()),
|
|
966
|
+
type: t.Optional(stringEnum([
|
|
967
|
+
"COLUMN",
|
|
968
|
+
"BAR",
|
|
969
|
+
"LINE",
|
|
970
|
+
"AREA",
|
|
971
|
+
"PIE",
|
|
972
|
+
"SCATTER",
|
|
973
|
+
"COMBO",
|
|
974
|
+
])),
|
|
975
|
+
sourceSheet: t.Optional(sheetReferenceSchema),
|
|
976
|
+
sourceRange: t.Optional(NonEmptyString),
|
|
977
|
+
legendPosition: t.Optional(stringEnum([
|
|
978
|
+
"BOTTOM_LEGEND",
|
|
979
|
+
"TOP_LEGEND",
|
|
980
|
+
"RIGHT_LEGEND",
|
|
981
|
+
"LEFT_LEGEND",
|
|
982
|
+
"NO_LEGEND",
|
|
983
|
+
])),
|
|
984
|
+
stackedType: t.Optional(stringEnum(["STACKED", "PERCENT_STACKED"])),
|
|
985
|
+
anchorSheet: sheetReferenceSchema,
|
|
986
|
+
anchorRow: t.Optional(NonNegativeInteger),
|
|
987
|
+
anchorColumn: t.Optional(NonNegativeInteger),
|
|
988
|
+
widthPx: t.Optional(PositiveInteger),
|
|
989
|
+
heightPx: t.Optional(PositiveInteger),
|
|
990
|
+
chartSpec: t.Optional(RawGoogleObject),
|
|
991
|
+
}, {
|
|
992
|
+
additionalProperties: false,
|
|
993
|
+
oneOf: [
|
|
994
|
+
{
|
|
995
|
+
required: ["chartSpec"],
|
|
996
|
+
not: {
|
|
997
|
+
anyOf: [
|
|
998
|
+
{ required: ["title"] },
|
|
999
|
+
{ required: ["type"] },
|
|
1000
|
+
{ required: ["sourceSheet"] },
|
|
1001
|
+
{ required: ["sourceRange"] },
|
|
1002
|
+
{ required: ["legendPosition"] },
|
|
1003
|
+
{ required: ["stackedType"] },
|
|
1004
|
+
],
|
|
1005
|
+
},
|
|
1006
|
+
},
|
|
1007
|
+
{
|
|
1008
|
+
required: ["sourceSheet", "sourceRange"],
|
|
1009
|
+
not: { required: ["chartSpec"] },
|
|
1010
|
+
},
|
|
1011
|
+
],
|
|
1012
|
+
}),
|
|
945
1013
|
async execute(input, ctx) {
|
|
946
1014
|
const p = (input ?? {});
|
|
947
1015
|
const spreadsheetId = p.spreadsheetId;
|
|
@@ -960,14 +1028,41 @@ async function runUpdateOrUpsert(ctx, p, upsert) {
|
|
|
960
1028
|
legendPosition: p.legendPosition ?? "BOTTOM_LEGEND",
|
|
961
1029
|
stackedType: p.stackedType,
|
|
962
1030
|
headerCount: 1,
|
|
963
|
-
domains: [
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
1031
|
+
domains: [
|
|
1032
|
+
{
|
|
1033
|
+
domain: {
|
|
1034
|
+
sourceRange: {
|
|
1035
|
+
sources: [
|
|
1036
|
+
{
|
|
1037
|
+
sheetId: grid.sheetId,
|
|
1038
|
+
startRowIndex: grid.startRowIndex,
|
|
1039
|
+
endRowIndex: grid.endRowIndex,
|
|
1040
|
+
startColumnIndex: grid.startColumnIndex,
|
|
1041
|
+
endColumnIndex: grid.startColumnIndex + 1,
|
|
1042
|
+
},
|
|
1043
|
+
],
|
|
1044
|
+
},
|
|
1045
|
+
},
|
|
1046
|
+
},
|
|
1047
|
+
],
|
|
1048
|
+
series: [
|
|
1049
|
+
{
|
|
1050
|
+
series: {
|
|
1051
|
+
sourceRange: {
|
|
1052
|
+
sources: [
|
|
1053
|
+
{
|
|
1054
|
+
sheetId: grid.sheetId,
|
|
1055
|
+
startRowIndex: grid.startRowIndex,
|
|
1056
|
+
endRowIndex: grid.endRowIndex,
|
|
1057
|
+
startColumnIndex: grid.startColumnIndex + 1,
|
|
1058
|
+
endColumnIndex: grid.endColumnIndex,
|
|
1059
|
+
},
|
|
1060
|
+
],
|
|
1061
|
+
},
|
|
1062
|
+
},
|
|
1063
|
+
targetAxis: "LEFT_AXIS",
|
|
1064
|
+
},
|
|
1065
|
+
],
|
|
971
1066
|
},
|
|
972
1067
|
};
|
|
973
1068
|
}
|
|
@@ -977,7 +1072,11 @@ async function runUpdateOrUpsert(ctx, p, upsert) {
|
|
|
977
1072
|
spec,
|
|
978
1073
|
position: {
|
|
979
1074
|
overlayPosition: {
|
|
980
|
-
anchorCell: {
|
|
1075
|
+
anchorCell: {
|
|
1076
|
+
sheetId: anchorSheetId,
|
|
1077
|
+
rowIndex: p.anchorRow ?? 0,
|
|
1078
|
+
columnIndex: p.anchorColumn ?? 0,
|
|
1079
|
+
},
|
|
981
1080
|
widthPixels: p.widthPx ?? 600,
|
|
982
1081
|
heightPixels: p.heightPx ?? 371,
|
|
983
1082
|
},
|
|
@@ -991,12 +1090,12 @@ async function runUpdateOrUpsert(ctx, p, upsert) {
|
|
|
991
1090
|
rl.registerAction("chart.update", {
|
|
992
1091
|
access: "write",
|
|
993
1092
|
description: "Update an existing chart's spec by chartId.",
|
|
994
|
-
inputSchema: {
|
|
995
|
-
spreadsheetId:
|
|
996
|
-
chartId:
|
|
997
|
-
chartSpec:
|
|
998
|
-
fields:
|
|
999
|
-
},
|
|
1093
|
+
inputSchema: t.Object({
|
|
1094
|
+
spreadsheetId: Id,
|
|
1095
|
+
chartId: NonNegativeInteger,
|
|
1096
|
+
chartSpec: RawGoogleObject,
|
|
1097
|
+
fields: t.Optional(NonEmptyString),
|
|
1098
|
+
}, { additionalProperties: false }),
|
|
1000
1099
|
async execute(input, ctx) {
|
|
1001
1100
|
const p = (input ?? {});
|
|
1002
1101
|
return spreadsheetBatchUpdate(ctx, p.spreadsheetId, [
|
|
@@ -1013,10 +1112,10 @@ async function runUpdateOrUpsert(ctx, p, upsert) {
|
|
|
1013
1112
|
rl.registerAction("chart.delete", {
|
|
1014
1113
|
access: "write",
|
|
1015
1114
|
description: "Delete a chart by its embedded-object id.",
|
|
1016
|
-
inputSchema: {
|
|
1017
|
-
spreadsheetId:
|
|
1018
|
-
chartId:
|
|
1019
|
-
},
|
|
1115
|
+
inputSchema: t.Object({
|
|
1116
|
+
spreadsheetId: Id,
|
|
1117
|
+
chartId: NonNegativeInteger,
|
|
1118
|
+
}, { additionalProperties: false }),
|
|
1020
1119
|
async execute(input, ctx) {
|
|
1021
1120
|
const p = (input ?? {});
|
|
1022
1121
|
return spreadsheetBatchUpdate(ctx, p.spreadsheetId, [
|
|
@@ -1028,12 +1127,12 @@ async function runUpdateOrUpsert(ctx, p, upsert) {
|
|
|
1028
1127
|
rl.registerAction("namedRange.add", {
|
|
1029
1128
|
access: "write",
|
|
1030
1129
|
description: "Create a named range on a sheet (so formulas can reference it by name).",
|
|
1031
|
-
inputSchema: {
|
|
1032
|
-
spreadsheetId:
|
|
1033
|
-
name:
|
|
1034
|
-
sheet:
|
|
1035
|
-
range:
|
|
1036
|
-
},
|
|
1130
|
+
inputSchema: t.Object({
|
|
1131
|
+
spreadsheetId: Id,
|
|
1132
|
+
name: NonEmptyString,
|
|
1133
|
+
sheet: sheetReferenceSchema,
|
|
1134
|
+
range: NonEmptyString,
|
|
1135
|
+
}, { additionalProperties: false }),
|
|
1037
1136
|
async execute(input, ctx) {
|
|
1038
1137
|
const p = (input ?? {});
|
|
1039
1138
|
const sheetId = await resolveSheetId(ctx, p.spreadsheetId, p.sheet);
|
|
@@ -1046,10 +1145,10 @@ async function runUpdateOrUpsert(ctx, p, upsert) {
|
|
|
1046
1145
|
rl.registerAction("namedRange.delete", {
|
|
1047
1146
|
access: "write",
|
|
1048
1147
|
description: "Delete a named range by its id.",
|
|
1049
|
-
inputSchema: {
|
|
1050
|
-
spreadsheetId:
|
|
1051
|
-
namedRangeId:
|
|
1052
|
-
},
|
|
1148
|
+
inputSchema: t.Object({
|
|
1149
|
+
spreadsheetId: Id,
|
|
1150
|
+
namedRangeId: Id,
|
|
1151
|
+
}, { additionalProperties: false }),
|
|
1053
1152
|
async execute(input, ctx) {
|
|
1054
1153
|
const p = (input ?? {});
|
|
1055
1154
|
return spreadsheetBatchUpdate(ctx, p.spreadsheetId, [
|
|
@@ -1061,18 +1160,21 @@ async function runUpdateOrUpsert(ctx, p, upsert) {
|
|
|
1061
1160
|
rl.registerAction("protectedRange.add", {
|
|
1062
1161
|
access: "write",
|
|
1063
1162
|
description: "Protect a range from edits. Pass `editorEmails` to restrict who can still edit; without it, only the sheet owner can edit.",
|
|
1064
|
-
inputSchema: {
|
|
1065
|
-
spreadsheetId:
|
|
1066
|
-
sheet:
|
|
1067
|
-
range:
|
|
1068
|
-
description:
|
|
1069
|
-
warningOnly:
|
|
1070
|
-
editorEmails:
|
|
1071
|
-
},
|
|
1163
|
+
inputSchema: t.Object({
|
|
1164
|
+
spreadsheetId: Id,
|
|
1165
|
+
sheet: sheetReferenceSchema,
|
|
1166
|
+
range: t.Optional(NonEmptyString),
|
|
1167
|
+
description: t.Optional(t.String()),
|
|
1168
|
+
warningOnly: t.Optional(t.Boolean()),
|
|
1169
|
+
editorEmails: t.Optional(t.Array(NonEmptyString)),
|
|
1170
|
+
}, { additionalProperties: false }),
|
|
1072
1171
|
async execute(input, ctx) {
|
|
1073
1172
|
const p = (input ?? {});
|
|
1074
1173
|
const sheetId = await resolveSheetId(ctx, p.spreadsheetId, p.sheet);
|
|
1075
|
-
const protectedRange = {
|
|
1174
|
+
const protectedRange = {
|
|
1175
|
+
description: p.description,
|
|
1176
|
+
warningOnly: p.warningOnly,
|
|
1177
|
+
};
|
|
1076
1178
|
if (p.range)
|
|
1077
1179
|
protectedRange.range = a1RangeToGridRange(p.range, sheetId);
|
|
1078
1180
|
else
|
|
@@ -1088,10 +1190,10 @@ async function runUpdateOrUpsert(ctx, p, upsert) {
|
|
|
1088
1190
|
rl.registerAction("protectedRange.delete", {
|
|
1089
1191
|
access: "write",
|
|
1090
1192
|
description: "Delete a protected range by its id.",
|
|
1091
|
-
inputSchema: {
|
|
1092
|
-
spreadsheetId:
|
|
1093
|
-
protectedRangeId:
|
|
1094
|
-
},
|
|
1193
|
+
inputSchema: t.Object({
|
|
1194
|
+
spreadsheetId: Id,
|
|
1195
|
+
protectedRangeId: NonNegativeInteger,
|
|
1196
|
+
}, { additionalProperties: false }),
|
|
1095
1197
|
async execute(input, ctx) {
|
|
1096
1198
|
const p = (input ?? {});
|
|
1097
1199
|
return spreadsheetBatchUpdate(ctx, p.spreadsheetId, [
|
|
@@ -1103,21 +1205,37 @@ async function runUpdateOrUpsert(ctx, p, upsert) {
|
|
|
1103
1205
|
rl.registerAction("conditionalFormat.add", {
|
|
1104
1206
|
access: "write",
|
|
1105
1207
|
description: "Add a single-condition conditional-format rule. For complex rules pass a raw `rule` object instead of the simple builder.",
|
|
1106
|
-
inputSchema: {
|
|
1107
|
-
spreadsheetId:
|
|
1108
|
-
sheet:
|
|
1109
|
-
range:
|
|
1110
|
-
condition:
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1208
|
+
inputSchema: t.Object({
|
|
1209
|
+
spreadsheetId: Id,
|
|
1210
|
+
sheet: sheetReferenceSchema,
|
|
1211
|
+
range: NonEmptyString,
|
|
1212
|
+
condition: t.Optional(RawGoogleObject),
|
|
1213
|
+
backgroundColorHex: t.Optional(t.String({ pattern: "^#?[0-9a-fA-F]{6}$" })),
|
|
1214
|
+
foregroundColorHex: t.Optional(t.String({ pattern: "^#?[0-9a-fA-F]{6}$" })),
|
|
1215
|
+
bold: t.Optional(t.Boolean()),
|
|
1216
|
+
italic: t.Optional(t.Boolean()),
|
|
1217
|
+
rule: t.Optional(RawGoogleObject),
|
|
1218
|
+
}, {
|
|
1219
|
+
additionalProperties: false,
|
|
1220
|
+
oneOf: [
|
|
1221
|
+
{
|
|
1222
|
+
required: ["rule"],
|
|
1223
|
+
not: {
|
|
1224
|
+
anyOf: [
|
|
1225
|
+
{ required: ["condition"] },
|
|
1226
|
+
{ required: ["backgroundColorHex"] },
|
|
1227
|
+
{ required: ["foregroundColorHex"] },
|
|
1228
|
+
{ required: ["bold"] },
|
|
1229
|
+
{ required: ["italic"] },
|
|
1230
|
+
],
|
|
1231
|
+
},
|
|
1232
|
+
},
|
|
1233
|
+
{
|
|
1234
|
+
required: ["condition"],
|
|
1235
|
+
not: { required: ["rule"] },
|
|
1236
|
+
},
|
|
1237
|
+
],
|
|
1238
|
+
}),
|
|
1121
1239
|
async execute(input, ctx) {
|
|
1122
1240
|
const p = (input ?? {});
|
|
1123
1241
|
const sheetId = await resolveSheetId(ctx, p.spreadsheetId, p.sheet);
|
|
@@ -1139,19 +1257,24 @@ async function runUpdateOrUpsert(ctx, p, upsert) {
|
|
|
1139
1257
|
ts.italic = p.italic;
|
|
1140
1258
|
fmt.textFormat = ts;
|
|
1141
1259
|
}
|
|
1142
|
-
rule = {
|
|
1260
|
+
rule = {
|
|
1261
|
+
ranges: [grid],
|
|
1262
|
+
booleanRule: { condition: p.condition, format: fmt },
|
|
1263
|
+
};
|
|
1143
1264
|
}
|
|
1144
|
-
return spreadsheetBatchUpdate(ctx, p.spreadsheetId, [
|
|
1265
|
+
return spreadsheetBatchUpdate(ctx, p.spreadsheetId, [
|
|
1266
|
+
{ addConditionalFormatRule: { rule, index: 0 } },
|
|
1267
|
+
]);
|
|
1145
1268
|
},
|
|
1146
1269
|
});
|
|
1147
1270
|
rl.registerAction("conditionalFormat.delete", {
|
|
1148
1271
|
access: "write",
|
|
1149
1272
|
description: "Delete a conditional-format rule by its index within the sheet's rule list.",
|
|
1150
|
-
inputSchema: {
|
|
1151
|
-
spreadsheetId:
|
|
1152
|
-
sheet:
|
|
1153
|
-
index:
|
|
1154
|
-
},
|
|
1273
|
+
inputSchema: t.Object({
|
|
1274
|
+
spreadsheetId: Id,
|
|
1275
|
+
sheet: sheetReferenceSchema,
|
|
1276
|
+
index: NonNegativeInteger,
|
|
1277
|
+
}, { additionalProperties: false }),
|
|
1155
1278
|
async execute(input, ctx) {
|
|
1156
1279
|
const p = (input ?? {});
|
|
1157
1280
|
const sheetId = await resolveSheetId(ctx, p.spreadsheetId, p.sheet);
|
|
@@ -1164,17 +1287,52 @@ async function runUpdateOrUpsert(ctx, p, upsert) {
|
|
|
1164
1287
|
rl.registerAction("dataValidation.set", {
|
|
1165
1288
|
access: "write",
|
|
1166
1289
|
description: "Apply a data-validation rule to a range. Pass either `condition` (BooleanCondition) or `oneOfList` (array of values for ONE_OF_LIST). Use `clear: true` to remove validation.",
|
|
1167
|
-
inputSchema: {
|
|
1168
|
-
spreadsheetId:
|
|
1169
|
-
sheet:
|
|
1170
|
-
range:
|
|
1171
|
-
condition:
|
|
1172
|
-
oneOfList: {
|
|
1173
|
-
showCustomUi:
|
|
1174
|
-
strict:
|
|
1175
|
-
inputMessage:
|
|
1176
|
-
clear:
|
|
1177
|
-
},
|
|
1290
|
+
inputSchema: t.Object({
|
|
1291
|
+
spreadsheetId: Id,
|
|
1292
|
+
sheet: sheetReferenceSchema,
|
|
1293
|
+
range: NonEmptyString,
|
|
1294
|
+
condition: t.Optional(RawGoogleObject),
|
|
1295
|
+
oneOfList: t.Optional(t.Array(JsonValue, { minItems: 1 })),
|
|
1296
|
+
showCustomUi: t.Optional(t.Boolean()),
|
|
1297
|
+
strict: t.Optional(t.Boolean()),
|
|
1298
|
+
inputMessage: t.Optional(t.String()),
|
|
1299
|
+
clear: t.Optional(t.Boolean()),
|
|
1300
|
+
}, {
|
|
1301
|
+
additionalProperties: false,
|
|
1302
|
+
oneOf: [
|
|
1303
|
+
{
|
|
1304
|
+
required: ["condition"],
|
|
1305
|
+
not: {
|
|
1306
|
+
anyOf: [
|
|
1307
|
+
{ required: ["oneOfList"] },
|
|
1308
|
+
{ required: ["clear"] },
|
|
1309
|
+
],
|
|
1310
|
+
},
|
|
1311
|
+
},
|
|
1312
|
+
{
|
|
1313
|
+
required: ["oneOfList"],
|
|
1314
|
+
not: {
|
|
1315
|
+
anyOf: [
|
|
1316
|
+
{ required: ["condition"] },
|
|
1317
|
+
{ required: ["clear"] },
|
|
1318
|
+
],
|
|
1319
|
+
},
|
|
1320
|
+
},
|
|
1321
|
+
{
|
|
1322
|
+
properties: { clear: { const: true } },
|
|
1323
|
+
required: ["clear"],
|
|
1324
|
+
not: {
|
|
1325
|
+
anyOf: [
|
|
1326
|
+
{ required: ["condition"] },
|
|
1327
|
+
{ required: ["oneOfList"] },
|
|
1328
|
+
{ required: ["showCustomUi"] },
|
|
1329
|
+
{ required: ["strict"] },
|
|
1330
|
+
{ required: ["inputMessage"] },
|
|
1331
|
+
],
|
|
1332
|
+
},
|
|
1333
|
+
},
|
|
1334
|
+
],
|
|
1335
|
+
}),
|
|
1178
1336
|
async execute(input, ctx) {
|
|
1179
1337
|
const p = (input ?? {});
|
|
1180
1338
|
const sheetId = await resolveSheetId(ctx, p.spreadsheetId, p.sheet);
|
|
@@ -1185,12 +1343,19 @@ async function runUpdateOrUpsert(ctx, p, upsert) {
|
|
|
1185
1343
|
if (!cond && Array.isArray(p.oneOfList)) {
|
|
1186
1344
|
cond = {
|
|
1187
1345
|
type: "ONE_OF_LIST",
|
|
1188
|
-
values: p.oneOfList.map((v) => ({
|
|
1346
|
+
values: p.oneOfList.map((v) => ({
|
|
1347
|
+
userEnteredValue: String(v),
|
|
1348
|
+
})),
|
|
1189
1349
|
};
|
|
1190
1350
|
}
|
|
1191
1351
|
if (!cond)
|
|
1192
1352
|
throw new Error("googleSheets.dataValidation.set: pass condition or oneOfList, or set clear=true");
|
|
1193
|
-
rule = {
|
|
1353
|
+
rule = {
|
|
1354
|
+
condition: cond,
|
|
1355
|
+
strict: p.strict ?? true,
|
|
1356
|
+
showCustomUi: p.showCustomUi ?? true,
|
|
1357
|
+
inputMessage: p.inputMessage,
|
|
1358
|
+
};
|
|
1194
1359
|
}
|
|
1195
1360
|
return spreadsheetBatchUpdate(ctx, p.spreadsheetId, [
|
|
1196
1361
|
{ setDataValidation: { range: grid, rule: rule ?? undefined } },
|