import * as z from "zod" import { defineAction } from "../../../automation/actions" import { assertSourceRowNumbers, buildA1Range, getGoogleSheetsApi, normalizeRowValues, normalizeSpreadsheetId, resolveRowSource, toGoogleCellData, } from "../lib/google-sheets" import { GOOGLE_SHEETS_WRITE_REQUIREMENT } from "../lib/scopes" import { ROW_MUTATION_RESULT_SCHEMA, ROW_SOURCE_SCHEMA, ROW_VALUES_INPUT_SCHEMA, SPREADSHEET_REFERENCE_SCHEMA, VALUE_INPUT_MODE_SCHEMA, } from "../lib/schemas" /** Inserts header-keyed rows at a specific one-based row number. */ export const insertGoogleSheetsRows = defineAction("Insert Google Sheets rows") .describe("Inserts rows and writes their header-keyed values atomically.") .account("google", GOOGLE_SHEETS_WRITE_REQUIREMENT) .input( z.object({ /** Whether strings are parsed like UI input or stored literally. */ inputMode: VALUE_INPUT_MODE_SCHEMA.optional(), /** One-based row number where insertion begins. */ rowNumber: z.number().int().positive(), /** One record or a non-empty list of records. */ rows: ROW_VALUES_INPUT_SCHEMA, /** Header-backed sheet or native table. */ source: ROW_SOURCE_SCHEMA, /** Spreadsheet ID or standard Google Sheets URL. */ spreadsheet: SPREADSHEET_REFERENCE_SCHEMA, }), ) .output(ROW_MUTATION_RESULT_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { const sheetsApi = getGoogleSheetsApi(account.secret) const spreadsheetId = normalizeSpreadsheetId(input.spreadsheet) const source = await resolveRowSource( sheetsApi, spreadsheetId, input.source, ) assertSourceRowNumbers(source, [input.rowNumber], { allowAppend: true }) const rows = Array.isArray(input.rows) ? input.rows : [input.rows] const startIndex = input.rowNumber - 1 await sheetsApi.spreadsheets.batchUpdate({ requestBody: { requests: [ { insertDimension: { inheritFromBefore: input.rowNumber > source.bodyStartRow, range: { dimension: "ROWS", endIndex: startIndex + rows.length, sheetId: source.sheetId, startIndex, }, }, }, { updateCells: { fields: "userEnteredValue", range: { endColumnIndex: source.columnCount, endRowIndex: startIndex + rows.length, sheetId: source.sheetId, startColumnIndex: 0, startRowIndex: startIndex, }, rows: rows.map((row) => ({ values: normalizeRowValues(row, source.headers).map((value) => toGoogleCellData(value, input.inputMode), ), })), }, }, ], }, spreadsheetId, }) return { affectedCells: rows.length * source.columnCount, rowNumbers: rows.map((_, index) => input.rowNumber + index), updatedRange: buildA1Range(source.sheetTitle, { endColumn: source.columnCount, endRow: input.rowNumber + rows.length - 1, startColumn: 1, startRow: input.rowNumber, }), } })