Skip to content
Draft
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { mkdtemp, readFile, rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import path from 'node:path'

import { afterEach, beforeEach, describe, expect, it } from 'vitest'

import { TelegramAlertChannelCodegen, type TelegramAlertChannelResource } from '../telegram-alert-channel-codegen.js'
import { Context } from '../internal/codegen/index.js'
import { Program } from '../../sourcegen/index.js'

describe('TelegramAlertChannelCodegen', () => {
let rootDirectory: string

beforeEach(async () => {
rootDirectory = await mkdtemp(path.join(tmpdir(), 'telegram-alert-channel-codegen-'))
})

afterEach(async () => {
await rm(rootDirectory, { recursive: true, force: true })
})

it('preserves the message thread ID when exporting a Telegram alert channel', async () => {
const program = new Program({
rootDirectory,
constructFileSuffix: '.check',
specFileSuffix: '.spec',
language: 'typescript',
})
const codegen = new TelegramAlertChannelCodegen(program)
const context = new Context()
const resource: TelegramAlertChannelResource = {
id: 123,
type: 'WEBHOOK',
config: {
name: 'Telegram topic',
webhookType: 'WEBHOOK_TELEGRAM',
url: 'https://api.telegram.org/bot123456:ABC/sendMessage',
method: 'POST',
headers: [],
template: 'chat_id=-701234567&message_thread_id=42&parse_mode=HTML&text=test',
},
sendRecovery: true,
sendFailure: true,
sendDegraded: false,
sslExpiry: false,
sslExpiryThreshold: 30,
}

codegen.prepare('telegram-topic', resource, context)
codegen.gencode('telegram-topic', resource, context)
await program.realize()

const [filePath] = program.paths
if (filePath === undefined) {
throw new Error('Codegen did not register a generated file')
}
const source = await readFile(filePath, 'utf8')

expect(source).toContain(`messageThreadId: '42'`)
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { beforeEach, describe, expect, it } from 'vitest'

import { Project } from '../project.js'
import { Session } from '../session.js'
import { TelegramAlertChannel } from '../telegram-alert-channel.js'

describe('TelegramAlertChannel', () => {
beforeEach(() => {
Session.project = new Project('project-id', {
name: 'Test Project',
repoUrl: 'https://github.com/checkly/checkly-cli',
})
})

it('includes the message thread ID in the Telegram request template', () => {
const channel = new TelegramAlertChannel('telegram-topic', {
name: 'Telegram topic',
apiKey: '123456:ABC',
chatId: '-701234567',
messageThreadId: '42',
payload: 'test',
})

expect(channel.synthesize()).toMatchObject({
type: 'WEBHOOK',
config: {
webhookType: 'WEBHOOK_TELEGRAM',
template: 'chat_id=-701234567&message_thread_id=42&parse_mode=HTML&text=test',
},
})
})

it('keeps the existing template shape when no message thread ID is configured', () => {
const channel = new TelegramAlertChannel('telegram-chat', {
name: 'Telegram chat',
apiKey: '123456:ABC',
chatId: '-701234567',
payload: 'test',
})

expect(channel.synthesize()).toMatchObject({
config: {
template: 'chat_id=-701234567&parse_mode=HTML&text=test',
},
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ function apiKeyFromUrl (url: string): string | undefined {

interface TemplateValues {
chatId?: string
messageThreadId?: string
text?: string
}

Expand All @@ -37,6 +38,7 @@ function parseTemplate (template: string): TemplateValues {

return {
chatId: singleValue('chat_id'),
messageThreadId: singleValue('message_thread_id'),
text: singleValue('text'),
}
}
Expand Down Expand Up @@ -110,13 +112,17 @@ export class TelegramAlertChannelCodegen extends Codegen<TelegramAlertChannelRes
}

if (config.template) {
const { chatId, text } = parseTemplate(config.template)
const { chatId, messageThreadId, text } = parseTemplate(config.template)
if (chatId) {
builder.string('chatId', chatId)
} else {
throw new Error(`Failed to extract Telegram Chat ID from webhook template: ${config.template}`)
}

if (messageThreadId) {
builder.string('messageThreadId', messageThreadId)
}

if (text) {
if (text !== TelegramAlertChannel.DEFAULT_PAYLOAD) {
builder.string('payload', text)
Expand Down
10 changes: 9 additions & 1 deletion packages/cli/src/constructs/telegram-alert-channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ export interface TelegramAlertChannelProps extends AlertChannelProps {
* {@link https://www.checklyhq.com/docs/integrations/alerts/telegram/}
*/
apiKey: string
/**
* The optional message thread ID of a Telegram forum topic.
* {@link https://core.telegram.org/bots/api#sendmessage}
*/
messageThreadId?: string
/**
* An optional custom payload. If not given,
* `TelegramAlertChannel.DEFAULT_PAYLOAD` will be used.
Expand Down Expand Up @@ -57,9 +62,12 @@ Tags: {{#each TAGS}} <i><b>{{this}}</b></i> {{#unless @last}},{{/unless}} {{/eac
this.webhookType = 'WEBHOOK_TELEGRAM'
this.method = 'POST'
const payload = props.payload ?? TelegramAlertChannel.DEFAULT_PAYLOAD
const messageThreadPart = props.messageThreadId
? `&message_thread_id=${props.messageThreadId}`
: ''
// For historical reasons the payload is not escaped even though it
// should be.
this.template = `chat_id=${props.chatId}&parse_mode=HTML&text=${payload}`
this.template = `chat_id=${props.chatId}${messageThreadPart}&parse_mode=HTML&text=${payload}`
this.url = `https://api.telegram.org/bot${props.apiKey}/sendMessage`
}

Expand Down
Loading