Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion integrations/googlecalendar/integration.definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as sdk from '@botpress/sdk'
import { actions, entities, configuration, configurations, identifier, events, secrets, states } from './definitions'

export const INTEGRATION_NAME = 'googlecalendar'
export const INTEGRATION_VERSION = '2.0.7'
export const INTEGRATION_VERSION = '2.0.8'

export default new sdk.IntegrationDefinition({
name: INTEGRATION_NAME,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,7 @@ export const oauthCallbackHandler = async ({ client, ctx, req, logger }: bp.Hand
authorizationCode,
})

await client.configureIntegration({ identifier: ctx.webhookId })

logger.forBot().info('Successfully authenticated with Google Calendar')
}
9 changes: 7 additions & 2 deletions integrations/gsheets/definitions/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ const appendValues = {
.string()
.title('Start Column')
.describe(
'The start column letter(s) (e.g. "A", "B", "AA"). The range will be constructed from this column row 1 to column row 100000.'
'The start column letter(s) (e.g. "A", "B", "AA"). Used to identify the table in the sheet — data will be appended after the last row of the detected table.'
),
majorDimension: _commonFields.majorDimension
.optional()
Expand Down Expand Up @@ -425,7 +425,12 @@ const createNamedRangeInSheet = {
input: {
schema: z.object({
sheetId: z.number().title('Sheet ID').describe('The ID of the sheet to create the named range in.'),
rangeName: z.string().title('Name').describe('The name of the named range.'),
rangeName: z
.string()
.title('Name')
.describe(
'The name of the named range. Must follow naming rules: cannot contain spaces, cannot start with a number, and cannot conflict with standard cell references (e.g., "A1", "R1C1"). Names violating these rules will be considered invalid.'
),
rangeA1: _commonFields.range.describe(
'The A1 notation of the range to associate with the named range. (e.g. "Sheet1!A1:B2")'
),
Expand Down
2 changes: 1 addition & 1 deletion integrations/gsheets/integration.definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
} from './definitions'

export const INTEGRATION_NAME = 'gsheets'
export const INTEGRATION_VERSION = '2.1.3'
export const INTEGRATION_VERSION = '2.1.5'

export default new sdk.IntegrationDefinition({
name: INTEGRATION_NAME,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
const MAX_ROW_NUMBER = 100000
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 ? `${sheetName}!` : ''
return `${sheetPrefix}${startColumn}:${startColumn}${MAX_ROW_NUMBER}`
const sheetPrefix = sheetName ? `${_quoteSheetName(sheetName)}!` : ''
return `${sheetPrefix}${startColumn}:${startColumn}`
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,50 @@ import { constructRangeFromStartColumn } from './append-values-utils'

describe.concurrent('constructRangeFromStartColumn', () => {
it('constructs range for simple column without sheet name', () => {
expect(constructRangeFromStartColumn(undefined, 'A')).toBe('A:A100000')
expect(constructRangeFromStartColumn(undefined, 'ZZ')).toBe('ZZ:ZZ100000')
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:A100000')
expect(constructRangeFromStartColumn('Sheet1', 'B')).toBe('Sheet1!B:B100000')
expect(constructRangeFromStartColumn('My Sheet', 'C')).toBe('My Sheet!C:C100000')
expect(constructRangeFromStartColumn('Sheet1', 'AA')).toBe('Sheet1!AA:AA100000')
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('handles sheet names with special characters', () => {
expect(constructRangeFromStartColumn("'My Sheet'", 'A')).toBe("'My Sheet'!A:A100000")
expect(constructRangeFromStartColumn("'Sheet 1'", 'B')).toBe("'Sheet 1'!B:B100000")
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('handles multi-character column names', () => {
expect(constructRangeFromStartColumn(undefined, 'AB')).toBe('AB:AB100000')
expect(constructRangeFromStartColumn(undefined, 'ABC')).toBe('ABC:ABC100000')
expect(constructRangeFromStartColumn('Sheet1', 'XYZ')).toBe('Sheet1!XYZ:XYZ100000')
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('handles empty string sheet name as undefined', () => {
expect(constructRangeFromStartColumn('', 'A')).toBe('A:A100000')
expect(constructRangeFromStartColumn('', 'B')).toBe('B:B100000')
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('preserves sheet name format exactly', () => {
expect(constructRangeFromStartColumn('Sheet1', 'A')).toBe('Sheet1!A:A100000')
expect(constructRangeFromStartColumn(' Sheet1 ', 'A')).toBe(' Sheet1 !A:A100000')
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')
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,7 @@ export const oauthCallbackHandler = async ({ client, ctx, req, logger }: bp.Hand
authorizationCode,
})

await client.configureIntegration({ identifier: ctx.webhookId })

logger.forBot().info('Successfully authenticated with Google Sheets')
}
4 changes: 2 additions & 2 deletions packages/cognitive/refresh-models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const filteredLifecycles = ['deprecated', 'discontinued']
const modelsListPath = path.resolve(__dirname, 'src/cognitive-v2', 'models.ts')
const typesPath = path.resolve(__dirname, 'src/cognitive-v2', 'types.ts')

type RemoteModel = Model & { lifecycle?: string; aliases?: string[] }
type RemoteModel = Model & { lifecycle?: string; aliases?: string[]; capabilities?: { supportsImages?: boolean } }

const toRef = (m: RemoteModel | string | null | undefined): string | null => {
if (!m) return null
Expand Down Expand Up @@ -46,7 +46,7 @@ async function main(): Promise<void> {
}

const newFile = `import { Model } from 'src/schemas.gen'\n
export type RemoteModel = Model & { aliases?: string[]; lifecycle: 'production' | 'preview' | 'deprecated' | 'discontinued' }\n
export type RemoteModel = Model & { aliases?: string[]; lifecycle: 'production' | 'preview' | 'deprecated' | 'discontinued'; capabilities?: { supportsImages?: boolean } }\n
export const models: Record<string, RemoteModel> = ${JSON.stringify(modelsObj, null, 2)}\n
export const defaultModel: RemoteModel = ${JSON.stringify(defaultModel, undefined, 2)}
`
Expand Down
Loading
Loading