chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:34:48 +08:00
commit 77bb5bf71f
3762 changed files with 353249 additions and 0 deletions
@@ -0,0 +1,24 @@
import { createActionWrapper } from '@botpress/common'
import { wrapAsyncFnWithTryCatch } from '../google-api/error-handling'
import { GoogleClient } from '../google-api/google-client'
import * as bp from '.botpress'
export const wrapAction: typeof _wrapAction = (meta, actionImpl) =>
_wrapAction(meta, (props) =>
wrapAsyncFnWithTryCatch(() => {
props.logger
.forBot()
.debug(`Running action "${meta.actionName}" [bot id: ${props.ctx.botId}]`, { input: props.input })
return actionImpl(props as Parameters<typeof actionImpl>[0], props.input)
}, `Action Error: ${meta.errorMessageWhenFailed}`)()
)
const _wrapAction = createActionWrapper<bp.IntegrationProps>()({
toolFactories: {
googleClient: GoogleClient.create,
},
extraMetadata: {} as {
errorMessageWhenFailed: string
},
})
@@ -0,0 +1,6 @@
import { wrapAction } from '../action-wrapper'
export const addSheet = wrapAction(
{ actionName: 'addSheet', errorMessageWhenFailed: 'Failed to add new sheet' },
async ({ googleClient }, { title: sheetTitle }) => await googleClient.createNewSheetInSpreadsheet({ sheetTitle })
)
@@ -0,0 +1,15 @@
const _quoteSheetName = (sheetName: string): string => {
if (sheetName.startsWith("'") && sheetName.endsWith("'")) {
return sheetName
}
if (/[^A-Za-z0-9_]/.test(sheetName)) {
const escaped = sheetName.replace(/'/g, "''")
return `'${escaped}'`
}
return sheetName
}
export const constructRangeFromStartColumn = (sheetName: string | undefined, startColumn: string): string => {
const sheetPrefix = sheetName ? `${_quoteSheetName(sheetName)}!` : ''
return `${sheetPrefix}${startColumn}:${startColumn}`
}
@@ -0,0 +1,52 @@
import { describe, it, expect } from 'vitest'
import { constructRangeFromStartColumn } from './append-values-utils'
describe.concurrent('constructRangeFromStartColumn', () => {
it('constructs range for simple column without sheet name', () => {
expect(constructRangeFromStartColumn(undefined, 'A')).toBe('A:A')
expect(constructRangeFromStartColumn(undefined, 'ZZ')).toBe('ZZ:ZZ')
})
it('constructs range for column with sheet name', () => {
expect(constructRangeFromStartColumn('Sheet1', 'A')).toBe('Sheet1!A:A')
expect(constructRangeFromStartColumn('Sheet1', 'B')).toBe('Sheet1!B:B')
expect(constructRangeFromStartColumn('Sheet1', 'AA')).toBe('Sheet1!AA:AA')
})
it('auto-quotes sheet names with spaces', () => {
expect(constructRangeFromStartColumn('My Sheet', 'A')).toBe("'My Sheet'!A:A")
expect(constructRangeFromStartColumn('Decision Log', 'C')).toBe("'Decision Log'!C:C")
})
it('does not double-quote already quoted sheet names', () => {
expect(constructRangeFromStartColumn("'My Sheet'", 'A')).toBe("'My Sheet'!A:A")
expect(constructRangeFromStartColumn("'Sheet 1'", 'B')).toBe("'Sheet 1'!B:B")
})
it('quotes sheet names with special characters', () => {
expect(constructRangeFromStartColumn('Sheet-1', 'A')).toBe("'Sheet-1'!A:A")
expect(constructRangeFromStartColumn('Sheet.1', 'A')).toBe("'Sheet.1'!A:A")
})
it('escapes single quotes (apostrophes) in sheet names', () => {
expect(constructRangeFromStartColumn("John's Data", 'A')).toBe("'John''s Data'!A:A")
expect(constructRangeFromStartColumn("It's a sheet", 'B')).toBe("'It''s a sheet'!B:B")
expect(constructRangeFromStartColumn("A'B'C", 'A')).toBe("'A''B''C'!A:A")
})
it('does not quote simple alphanumeric/underscore sheet names', () => {
expect(constructRangeFromStartColumn('Sheet1', 'A')).toBe('Sheet1!A:A')
expect(constructRangeFromStartColumn('My_Sheet', 'A')).toBe('My_Sheet!A:A')
})
it('handles multi-character column names', () => {
expect(constructRangeFromStartColumn(undefined, 'AB')).toBe('AB:AB')
expect(constructRangeFromStartColumn(undefined, 'ABC')).toBe('ABC:ABC')
expect(constructRangeFromStartColumn('Sheet1', 'XYZ')).toBe('Sheet1!XYZ:XYZ')
})
it('handles empty string sheet name as undefined', () => {
expect(constructRangeFromStartColumn('', 'A')).toBe('A:A')
expect(constructRangeFromStartColumn('', 'B')).toBe('B:B')
})
})
@@ -0,0 +1,12 @@
import { wrapAction } from '../action-wrapper'
import { constructRangeFromStartColumn } from './append-values-utils'
export const appendValues = wrapAction(
{ actionName: 'appendValues', errorMessageWhenFailed: 'Failed to append values into spreadsheet' },
async ({ googleClient }, { sheetName, startColumn, values, majorDimension }) =>
await googleClient.appendValuesToSpreadsheetRange({
rangeA1: constructRangeFromStartColumn(sheetName, startColumn),
values,
majorDimension,
})
)
@@ -0,0 +1,6 @@
import { wrapAction } from '../action-wrapper'
export const clearValues = wrapAction(
{ actionName: 'clearValues', errorMessageWhenFailed: 'Failed to clear values from the spreadsheet' },
async ({ googleClient }, { range: rangeA1 }) => await googleClient.clearValuesFromSpreadsheetRange({ rangeA1 })
)
@@ -0,0 +1,7 @@
import { wrapAction } from '../action-wrapper'
export const createNamedRangeInSheet = wrapAction(
{ actionName: 'createNamedRangeInSheet', errorMessageWhenFailed: 'Failed to add named range to sheet' },
async ({ googleClient }, { rangeName, rangeA1, sheetId }) =>
await googleClient.createNamedRangeInSheet({ a1Notation: rangeA1, rangeName, sheetId })
)
@@ -0,0 +1,21 @@
import { wrapAction } from '../action-wrapper'
export const deleteRows = wrapAction(
{ actionName: 'deleteRows', errorMessageWhenFailed: 'Failed to delete rows' },
async ({ googleClient }, { sheetName, rowIndexes }) => {
if (rowIndexes.length === 0) {
return { deletedCount: 0 }
}
const { sheetId } = await googleClient.getSheetIdByName(sheetName)
await googleClient.deleteRowsFromSheet({
sheetId,
rowIndexes,
})
return {
deletedCount: rowIndexes.length,
}
}
)
@@ -0,0 +1,6 @@
import { wrapAction } from '../action-wrapper'
export const deleteSheet = wrapAction(
{ actionName: 'deleteSheet', errorMessageWhenFailed: 'Failed to delete sheet from spreadsheet' },
async ({ googleClient }, { sheetId }) => await googleClient.deleteSheetFromSpreadsheet({ sheetId })
)
@@ -0,0 +1,43 @@
import { wrapAction } from '../action-wrapper'
import { buildSheetPrefix, columnLetterToIndex } from './row-utils'
export const findRow = wrapAction(
{ actionName: 'findRow', errorMessageWhenFailed: 'Failed to find row' },
async ({ googleClient }, { sheetName, searchColumn, searchValue, dataRange }) => {
const { sheetTitle } = await googleClient.getSheetIdByName(sheetName)
const prefix = buildSheetPrefix(sheetTitle)
const rangeA1 = dataRange ? `${prefix}${dataRange.split('!').pop()}` : `${prefix}A:ZZ`
let values: string[][] = []
try {
const result = await googleClient.getValuesFromSpreadsheetRange({ rangeA1, majorDimension: 'ROWS' })
values = result.values ?? []
} catch {
return { found: false, row: null }
}
if (values.length === 0) {
return { found: false, row: null }
}
const searchColumnIndex = columnLetterToIndex(searchColumn)
for (const [i, row] of values.entries()) {
const rowValues = row ?? []
const cellValue = rowValues[searchColumnIndex] ?? ''
if (cellValue === searchValue) {
return {
found: true,
row: {
rowIndex: i + 1,
values: rowValues.map(String),
},
}
}
}
return { found: false, row: null }
}
)
@@ -0,0 +1,44 @@
import { wrapAction } from '../action-wrapper'
import { buildSheetPrefix, columnLetterToIndex } from './row-utils'
export const findRows = wrapAction(
{ actionName: 'findRows', errorMessageWhenFailed: 'Failed to find rows' },
async ({ googleClient }, { sheetName, searchColumn, searchValue, dataRange }) => {
const { sheetTitle } = await googleClient.getSheetIdByName(sheetName)
const prefix = buildSheetPrefix(sheetTitle)
const rangeA1 = dataRange ? `${prefix}${dataRange.split('!').pop()}` : `${prefix}A:ZZ`
let values: string[][] = []
try {
const result = await googleClient.getValuesFromSpreadsheetRange({ rangeA1, majorDimension: 'ROWS' })
values = result.values ?? []
} catch {
return { rows: [], totalMatches: 0 }
}
if (values.length === 0) {
return { rows: [], totalMatches: 0 }
}
const searchColumnIndex = columnLetterToIndex(searchColumn)
const rows: Array<{ rowIndex: number; values: string[] }> = []
for (const [i, row] of values.entries()) {
const rowValues = row ?? []
const cellValue = rowValues[searchColumnIndex] ?? ''
if (cellValue === searchValue) {
rows.push({
rowIndex: i + 1,
values: rowValues.map(String),
})
}
}
return {
rows,
totalMatches: rows.length,
}
}
)
@@ -0,0 +1,8 @@
import { wrapAction } from '../action-wrapper'
export const getAllSheetsInSpreadsheet = wrapAction(
{ actionName: 'getAllSheetsInSpreadsheet', errorMessageWhenFailed: 'Failed to obtain sheets' },
async ({ googleClient }) => ({
sheets: await googleClient.getAllSheetsInSpreadsheet(),
})
)
@@ -0,0 +1,6 @@
import { wrapAction } from '../action-wrapper'
export const getInfoSpreadsheet = wrapAction(
{ actionName: 'getInfoSpreadsheet', errorMessageWhenFailed: 'Failed to get spreadsheet info' },
async ({ googleClient }, { fields }) => await googleClient.getSpreadsheetMetadata({ fields: fields.join(',') })
)
@@ -0,0 +1,8 @@
import { wrapAction } from '../action-wrapper'
export const getNamedRanges = wrapAction(
{ actionName: 'getNamedRanges', errorMessageWhenFailed: 'Failed to obtain the named ranges of the spreadsheet' },
async ({ googleClient }) => ({
namedRanges: await googleClient.getNamedRanges(),
})
)
@@ -0,0 +1,11 @@
import { wrapAction } from '../action-wrapper'
export const getProtectedRanges = wrapAction(
{
actionName: 'getProtectedRanges',
errorMessageWhenFailed: 'Failed to obtain the protected ranges of the spreadsheet',
},
async ({ googleClient }) => ({
protectedRanges: await googleClient.getProtectedRanges(),
})
)
@@ -0,0 +1,36 @@
import { wrapAction } from '../action-wrapper'
import { buildRowRange } from './row-utils'
export const getRow = wrapAction(
{ actionName: 'getRow', errorMessageWhenFailed: 'Failed to get row' },
async ({ googleClient }, { sheetName, rowIndex, startColumn, endColumn }) => {
const { sheetTitle } = await googleClient.getSheetIdByName(sheetName)
const rangeA1 = buildRowRange({
sheetTitle,
rowIndex,
startColumn: startColumn ?? 'A',
endColumn,
})
let values: string[][] = []
try {
const result = await googleClient.getValuesFromSpreadsheetRange({ rangeA1, majorDimension: 'ROWS' })
values = result.values ?? []
} catch {
return { found: false, row: null }
}
if (values.length === 0 || !values[0] || values[0].length === 0) {
return { found: false, row: null }
}
return {
found: true,
row: {
rowIndex,
values: values[0].map(String),
},
}
}
)
@@ -0,0 +1,10 @@
import { wrapAction } from '../action-wrapper'
export const getValues = wrapAction(
{ actionName: 'getValues', errorMessageWhenFailed: 'Failed to get values from the specified range' },
async ({ googleClient }, { range: rangeA1, majorDimension }) =>
await googleClient.getValuesFromSpreadsheetRange({
rangeA1,
majorDimension,
})
)
@@ -0,0 +1,39 @@
import { wrapAction } from '../action-wrapper'
import { buildRowRange, indexToColumnLetter, columnLetterToIndex } from './row-utils'
export const insertRowAtIndex = wrapAction(
{ actionName: 'insertRowAtIndex', errorMessageWhenFailed: 'Failed to insert row at index' },
async ({ googleClient }, { sheetName, rowIndex, values, startColumn }) => {
const { sheetId, sheetTitle } = await googleClient.getSheetIdByName(sheetName)
await googleClient.insertRows({
sheetId,
startIndex: rowIndex - 1,
numberOfRows: 1,
})
if (values && values.length > 0) {
const start = startColumn ?? 'A'
const startColIndex = columnLetterToIndex(start)
const endColIndex = startColIndex + values.length - 1
const endColumn = indexToColumnLetter(endColIndex)
const rangeA1 = buildRowRange({
sheetTitle,
rowIndex,
startColumn: start,
endColumn,
})
await googleClient.updateValuesInSpreadsheetRange({
rangeA1,
values: [values],
majorDimension: 'ROWS',
})
}
return {
insertedRowIndex: rowIndex,
}
}
)
@@ -0,0 +1,6 @@
import { wrapAction } from '../action-wrapper'
export const moveSheetHorizontally = wrapAction(
{ actionName: 'moveSheetHorizontally', errorMessageWhenFailed: 'Failed to move sheet to the new index' },
async ({ googleClient }, { sheetId, newIndex }) => await googleClient.moveSheetToIndex({ sheetId, newIndex })
)
@@ -0,0 +1,7 @@
import { wrapAction } from '../action-wrapper'
export const protectNamedRange = wrapAction(
{ actionName: 'protectNamedRange', errorMessageWhenFailed: 'Failed to protect the named range' },
async ({ googleClient }, { namedRangeId, requestingUserCanEdit, warningOnly }) =>
await googleClient.protectNamedRange({ namedRangeId, requestingUserCanEdit, warningOnly })
)
@@ -0,0 +1,6 @@
import { wrapAction } from '../action-wrapper'
export const renameSheet = wrapAction(
{ actionName: 'renameSheet', errorMessageWhenFailed: 'Failed to rename sheet' },
async ({ googleClient }, { sheetId, newTitle }) => await googleClient.renameSheetInSpreadsheet({ sheetId, newTitle })
)
@@ -0,0 +1,129 @@
import { describe, it, expect } from 'vitest'
import {
columnLetterToIndex,
indexToColumnLetter,
buildSheetPrefix,
buildRowRange,
buildColumnRange,
} from './row-utils'
describe.concurrent('columnLetterToIndex', () => {
it('converts single letters A-Z to indices 0-25', () => {
expect(columnLetterToIndex('A')).toBe(0)
expect(columnLetterToIndex('B')).toBe(1)
expect(columnLetterToIndex('Z')).toBe(25)
})
it('converts double letters starting with AA', () => {
expect(columnLetterToIndex('AA')).toBe(26)
expect(columnLetterToIndex('AB')).toBe(27)
expect(columnLetterToIndex('AZ')).toBe(51)
})
it('converts BA and beyond', () => {
expect(columnLetterToIndex('BA')).toBe(52)
expect(columnLetterToIndex('ZZ')).toBe(701)
})
it('is case-insensitive', () => {
expect(columnLetterToIndex('a')).toBe(0)
expect(columnLetterToIndex('aa')).toBe(26)
expect(columnLetterToIndex('Aa')).toBe(26)
})
})
describe.concurrent('indexToColumnLetter', () => {
it('converts indices 0-25 to single letters A-Z', () => {
expect(indexToColumnLetter(0)).toBe('A')
expect(indexToColumnLetter(1)).toBe('B')
expect(indexToColumnLetter(25)).toBe('Z')
})
it('converts index 26+ to double letters', () => {
expect(indexToColumnLetter(26)).toBe('AA')
expect(indexToColumnLetter(27)).toBe('AB')
expect(indexToColumnLetter(51)).toBe('AZ')
})
it('converts higher indices correctly', () => {
expect(indexToColumnLetter(52)).toBe('BA')
expect(indexToColumnLetter(701)).toBe('ZZ')
})
})
describe.concurrent('columnLetterToIndex and indexToColumnLetter roundtrip', () => {
it('indexToColumnLetter inverts columnLetterToIndex', () => {
const letters = ['A', 'Z', 'AA', 'AZ', 'BA', 'ZZ']
for (const letter of letters) {
expect(indexToColumnLetter(columnLetterToIndex(letter))).toBe(letter)
}
})
it('columnLetterToIndex inverts indexToColumnLetter', () => {
const indices = [0, 25, 26, 51, 52, 701]
for (const index of indices) {
expect(columnLetterToIndex(indexToColumnLetter(index))).toBe(index)
}
})
})
describe.concurrent('buildSheetPrefix', () => {
it('returns empty string when no sheet title', () => {
expect(buildSheetPrefix()).toBe('')
expect(buildSheetPrefix(undefined)).toBe('')
})
it('returns simple prefix for alphanumeric titles', () => {
expect(buildSheetPrefix('Sheet1')).toBe('Sheet1!')
expect(buildSheetPrefix('Data')).toBe('Data!')
})
it('quotes titles containing spaces', () => {
expect(buildSheetPrefix('My Sheet')).toBe("'My Sheet'!")
})
it('quotes and escapes titles containing apostrophes', () => {
expect(buildSheetPrefix("It's")).toBe("'It''s'!")
expect(buildSheetPrefix("Don't stop")).toBe("'Don''t stop'!")
})
})
describe.concurrent('buildRowRange', () => {
it('builds simple row range with defaults', () => {
expect(buildRowRange({ rowIndex: 1 })).toBe('A1:1')
})
it('builds row range with explicit start and end columns', () => {
expect(buildRowRange({ rowIndex: 5, startColumn: 'B', endColumn: 'D' })).toBe('B5:D5')
})
it('includes sheet prefix when provided', () => {
expect(buildRowRange({ sheetTitle: 'Data', rowIndex: 3, startColumn: 'A', endColumn: 'C' })).toBe('Data!A3:C3')
})
it('handles sheet titles requiring quotes', () => {
expect(buildRowRange({ sheetTitle: 'My Data', rowIndex: 1, endColumn: 'B' })).toBe("'My Data'!A1:B1")
})
})
describe.concurrent('buildColumnRange', () => {
it('builds single column range with defaults', () => {
expect(buildColumnRange({})).toBe('A1:A100000')
})
it('builds range for specified columns', () => {
expect(buildColumnRange({ startColumn: 'B', endColumn: 'D' })).toBe('B1:D100000')
})
it('uses startColumn as endColumn when endColumn not specified', () => {
expect(buildColumnRange({ startColumn: 'C' })).toBe('C1:C100000')
})
it('respects custom maxRow', () => {
expect(buildColumnRange({ startColumn: 'A', endColumn: 'B', maxRow: 500 })).toBe('A1:B500')
})
it('includes sheet prefix when provided', () => {
expect(buildColumnRange({ sheetTitle: 'Sales', startColumn: 'A', endColumn: 'C' })).toBe('Sales!A1:C100000')
})
})
@@ -0,0 +1,58 @@
const ASCII_UPPERCASE_A = 65
const BASE_26 = 26
export const columnLetterToIndex = (col: string): number =>
col
.toUpperCase()
.split('')
.reduce((acc, char) => acc * BASE_26 + char.charCodeAt(0) - ASCII_UPPERCASE_A + 1, 0) - 1
export const indexToColumnLetter = (index: number): string => {
let letter = ''
let i = index
while (i >= 0) {
letter = String.fromCharCode((i % BASE_26) + ASCII_UPPERCASE_A) + letter
i = Math.floor(i / BASE_26) - 1
}
return letter
}
export const buildSheetPrefix = (sheetTitle?: string): string => {
if (!sheetTitle) {
return ''
}
const needsQuotes = sheetTitle.includes(' ') || sheetTitle.includes("'")
return needsQuotes ? `'${sheetTitle.replace(/'/g, "''")}'!` : `${sheetTitle}!`
}
export const buildRowRange = ({
sheetTitle,
rowIndex,
startColumn = 'A',
endColumn,
}: {
sheetTitle?: string
rowIndex: number
startColumn?: string
endColumn?: string
}): string => {
const prefix = buildSheetPrefix(sheetTitle)
const end = endColumn ? `${endColumn}${rowIndex}` : `${rowIndex}`
return `${prefix}${startColumn}${rowIndex}:${end}`
}
export const buildColumnRange = ({
sheetTitle,
startColumn = 'A',
endColumn,
maxRow = 100000,
}: {
sheetTitle?: string
startColumn?: string
endColumn?: string
maxRow?: number
}): string => {
const prefix = buildSheetPrefix(sheetTitle)
const end = endColumn ?? startColumn
return `${prefix}${startColumn}1:${end}${maxRow}`
}
@@ -0,0 +1,6 @@
import { wrapAction } from '../action-wrapper'
export const setSheetVisibility = wrapAction(
{ actionName: 'setSheetVisibility', errorMessageWhenFailed: 'Failed to set sheet visibility' },
async ({ googleClient }, { sheetId, isHidden }) => await googleClient.setSheetVisibility({ sheetId, isHidden })
)
@@ -0,0 +1,11 @@
import { wrapAction } from '../action-wrapper'
export const setValues = wrapAction(
{ actionName: 'setValues', errorMessageWhenFailed: 'Failed to set values in the specified range' },
async ({ googleClient }, { range: rangeA1, majorDimension, values }) =>
await googleClient.updateValuesInSpreadsheetRange({
rangeA1,
majorDimension,
values,
})
)
@@ -0,0 +1,6 @@
import { wrapAction } from '../action-wrapper'
export const unprotectRange = wrapAction(
{ actionName: 'unprotectRange', errorMessageWhenFailed: 'Failed to delete protected range' },
async ({ googleClient }, { protectedRangeId }) => await googleClient.unprotectRange({ protectedRangeId })
)
@@ -0,0 +1,32 @@
import { wrapAction } from '../action-wrapper'
import { buildRowRange, indexToColumnLetter, columnLetterToIndex } from './row-utils'
export const updateRow = wrapAction(
{ actionName: 'updateRow', errorMessageWhenFailed: 'Failed to update row' },
async ({ googleClient }, { sheetName, rowIndex, values, startColumn }) => {
const { sheetTitle } = await googleClient.getSheetIdByName(sheetName)
const start = startColumn ?? 'A'
const startColIndex = columnLetterToIndex(start)
const endColIndex = startColIndex + values.length - 1
const endColumn = indexToColumnLetter(endColIndex)
const rangeA1 = buildRowRange({
sheetTitle,
rowIndex,
startColumn: start,
endColumn,
})
const result = await googleClient.updateValuesInSpreadsheetRange({
rangeA1,
values: [values],
majorDimension: 'ROWS',
})
return {
updatedRange: result.updatedRange,
updatedCells: result.updatedCells,
}
}
)
@@ -0,0 +1,75 @@
import { wrapAction } from '../action-wrapper'
import { buildSheetPrefix, buildRowRange, columnLetterToIndex, indexToColumnLetter } from './row-utils'
export const upsertRow = wrapAction(
{ actionName: 'upsertRow', errorMessageWhenFailed: 'Failed to upsert row' },
async ({ googleClient }, { sheetName, keyColumn, keyValue, values, startColumn }) => {
const { sheetTitle } = await googleClient.getSheetIdByName(sheetName)
const prefix = buildSheetPrefix(sheetTitle)
const rangeA1 = `${prefix}A:ZZ`
let existingValues: string[][] = []
try {
const result = await googleClient.getValuesFromSpreadsheetRange({ rangeA1, majorDimension: 'ROWS' })
existingValues = result.values ?? []
} catch {
existingValues = []
}
const keyColumnIndex = columnLetterToIndex(keyColumn)
let matchingRowIndex: number | null = null
for (const [i, row] of existingValues.entries()) {
const rowValues = row ?? []
const cellValue = rowValues[keyColumnIndex] ?? ''
if (cellValue === keyValue) {
matchingRowIndex = i + 1
break
}
}
const start = startColumn ?? 'A'
const startColIndex = columnLetterToIndex(start)
const endColIndex = startColIndex + values.length - 1
const endColumn = indexToColumnLetter(endColIndex)
if (matchingRowIndex !== null) {
const updateRangeA1 = buildRowRange({
sheetTitle,
rowIndex: matchingRowIndex,
startColumn: start,
endColumn,
})
await googleClient.updateValuesInSpreadsheetRange({
rangeA1: updateRangeA1,
values: [values],
majorDimension: 'ROWS',
})
return {
action: 'updated' as const,
rowIndex: matchingRowIndex,
}
}
const appendRangeA1 = `${prefix}${start}:${endColumn}`
const appendResult = await googleClient.appendValuesToSpreadsheetRange({
rangeA1: appendRangeA1,
values: [values],
majorDimension: 'ROWS',
})
const updatedRange = appendResult.updates.updatedRange
const rowMatch = updatedRange.match(/:?[A-Z]+(\d+)$/)
const insertedRowIndex = rowMatch?.[1] ? parseInt(rowMatch[1], 10) : existingValues.length + 1
return {
action: 'inserted' as const,
rowIndex: insertedRowIndex,
}
}
)
+50
View File
@@ -0,0 +1,50 @@
import { addSheet } from './implementations/add-sheet'
import { appendValues } from './implementations/append-values'
import { clearValues } from './implementations/clear-values'
import { createNamedRangeInSheet } from './implementations/create-named-range-in-sheet'
import { deleteRows } from './implementations/delete-rows'
import { deleteSheet } from './implementations/delete-sheet'
import { findRow } from './implementations/find-row'
import { findRows } from './implementations/find-rows'
import { getAllSheetsInSpreadsheet } from './implementations/get-all-sheets-in-spreadsheet'
import { getInfoSpreadsheet } from './implementations/get-info-spread-sheet'
import { getNamedRanges } from './implementations/get-named-ranges'
import { getProtectedRanges } from './implementations/get-protected-ranges'
import { getRow } from './implementations/get-row'
import { getValues } from './implementations/get-values'
import { insertRowAtIndex } from './implementations/insert-row-at-index'
import { moveSheetHorizontally } from './implementations/move-sheet-horizontally'
import { protectNamedRange } from './implementations/protect-named-range'
import { renameSheet } from './implementations/rename-sheet'
import { setSheetVisibility } from './implementations/set-sheet-visibility'
import { setValues } from './implementations/set-values'
import { unprotectRange } from './implementations/unprotect-range'
import { updateRow } from './implementations/update-row'
import { upsertRow } from './implementations/upsert-row'
import * as bp from '.botpress'
export default {
addSheet,
appendValues,
clearValues,
createNamedRangeInSheet,
deleteRows,
deleteSheet,
findRow,
findRows,
getAllSheetsInSpreadsheet,
getInfoSpreadsheet,
getNamedRanges,
getProtectedRanges,
getRow,
getValues,
insertRowAtIndex,
moveSheetHorizontally,
protectNamedRange,
renameSheet,
setSheetVisibility,
setValues,
unprotectRange,
updateRow,
upsertRow,
} as const satisfies bp.IntegrationProps['actions']