Skip to content

Commit

Permalink
fix(telegram): parse correctly ChatID and MessageThreadID
Browse files Browse the repository at this point in the history
With this commit, the ChatID and MessageThreadID configuration of the
Telegram notifier now can be configured either with a string or an
integer, without any breaking changes for anyone consuming this library
or the end users using it in their configuration files.

Fixes grafana/grafana#69950
  • Loading branch information
NefixEstrada committed Jun 5, 2024
1 parent 792918f commit 814d7af
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 14 deletions.
55 changes: 51 additions & 4 deletions receivers/telegram/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,34 +28,81 @@ type Config struct {
DisableNotifications bool `json:"disable_notifications,omitempty" yaml:"disable_notifications,omitempty"`
}

type unmarshalConfig struct {
BotToken string `json:"bottoken,omitempty" yaml:"bottoken,omitempty"`
ChatID any `json:"chatid,omitempty" yaml:"chatid,omitempty"`
MessageThreadID any `json:"message_thread_id,omitempty" yaml:"message_thread_id,omitempty"`
Message string `json:"message,omitempty" yaml:"message,omitempty"`
ParseMode string `json:"parse_mode,omitempty" yaml:"parse_mode,omitempty"`
DisableWebPagePreview bool `json:"disable_web_page_preview,omitempty" yaml:"disable_web_page_preview,omitempty"`
ProtectContent bool `json:"protect_content,omitempty" yaml:"protect_content,omitempty"`
DisableNotifications bool `json:"disable_notifications,omitempty" yaml:"disable_notifications,omitempty"`
}

func NewConfig(jsonData json.RawMessage, decryptFn receivers.DecryptFunc) (Config, error) {
settings := Config{}
err := json.Unmarshal(jsonData, &settings)

unmarshaledConfig := unmarshalConfig{}
err := json.Unmarshal(jsonData, &unmarshaledConfig)
if err != nil {
return settings, fmt.Errorf("failed to unmarshal settings: %w", err)
}

settings.BotToken = unmarshaledConfig.BotToken
settings.Message = unmarshaledConfig.Message
settings.ParseMode = unmarshaledConfig.ParseMode
settings.DisableWebPagePreview = unmarshaledConfig.DisableWebPagePreview
settings.ProtectContent = unmarshaledConfig.ProtectContent
settings.DisableNotifications = unmarshaledConfig.DisableNotifications

settings.BotToken = decryptFn("bottoken", settings.BotToken)
if settings.BotToken == "" {
return settings, errors.New("could not find Bot Token in settings")
}
if settings.ChatID == "" {

switch chatID := unmarshaledConfig.ChatID.(type) {
case string:
settings.ChatID = chatID

case float64:
settings.ChatID = strconv.Itoa(int(chatID))

case nil:
return settings, errors.New("could not find Chat Id in settings")

default:
return settings, errors.New("chat id must be either a string or an int")
}

if settings.Message == "" {
settings.Message = templates.DefaultMessageEmbed
}

var messageThreadID int
if settings.MessageThreadID != "" {
messageThreadID, err = strconv.Atoi(settings.MessageThreadID)
switch id := unmarshaledConfig.MessageThreadID.(type) {
case string:
messageThreadID, err = strconv.Atoi(id)
if err != nil {
return settings, errors.New("message thread id must be an integer")
}

case float64:
messageThreadID = int(id)

case nil:

default:
return settings, errors.New("message thread id must be either a string or an int")
}

if messageThreadID != 0 {
if messageThreadID != int(int32(messageThreadID)) {
return settings, errors.New("message thread id must be an int32")
}

settings.MessageThreadID = strconv.Itoa(messageThreadID)
}

// if field is missing, then we fall back to the previous default: HTML
if settings.ParseMode == "" {
settings.ParseMode = DefaultTelegramParseMode
Expand Down
73 changes: 63 additions & 10 deletions receivers/telegram/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,33 @@ func TestNewConfig(t *testing.T) {
settings: `{ "bottoken" : "12345" }`,
expectedInitError: `could not find Chat Id in settings`,
},

{
name: "Error if chatid is not a string or an int",
settings: `{ "bottoken": "12345", "chatid" : {} }`,
expectedInitError: `chat id must be either a string or an int`,
},
{
name: "should be able to parse an int ChatID",
settings: `{"chatid": 12345678}`,
secureSettings: map[string][]byte{
"bottoken": []byte("test-token"),
},
expectedConfig: Config{
BotToken: "test-token",
ChatID: "12345678",
Message: templates.DefaultMessageEmbed,
ParseMode: "HTML",
DisableWebPagePreview: false,
ProtectContent: false,
DisableNotifications: false,
},
},
{
name: "Minimal valid configuration",
settings: `{ "bottoken": "test-token", "chatid": "test-chat-id" }`,
settings: `{ "bottoken": "test-token", "chatid": "-1312" }`,
expectedConfig: Config{
BotToken: "test-token",
ChatID: "test-chat-id",
ChatID: "-1312",
Message: templates.DefaultMessageEmbed,
ParseMode: DefaultTelegramParseMode,
DisableWebPagePreview: false,
Expand All @@ -54,13 +74,13 @@ func TestNewConfig(t *testing.T) {
},
{
name: "Minimal valid configuration from secrets",
settings: `{"chatid": "test-chat-id" }`,
settings: `{"chatid": "-1312" }`,
secureSettings: map[string][]byte{
"bottoken": []byte("test-token"),
},
expectedConfig: Config{
BotToken: "test-token",
ChatID: "test-chat-id",
ChatID: "-1312",
Message: templates.DefaultMessageEmbed,
ParseMode: DefaultTelegramParseMode,
DisableWebPagePreview: false,
Expand All @@ -70,13 +90,13 @@ func TestNewConfig(t *testing.T) {
},
{
name: "Should overwrite token from secrets",
settings: `{"bottoken": "token", "chatid" : "test-chat-id" }`,
settings: `{"bottoken": "token", "chatid" : "-1312" }`,
secureSettings: map[string][]byte{
"bottoken": []byte("test-token-key"),
},
expectedConfig: Config{
BotToken: "test-token-key",
ChatID: "test-chat-id",
ChatID: "-1312",
Message: templates.DefaultMessageEmbed,
ParseMode: DefaultTelegramParseMode,
DisableWebPagePreview: false,
Expand All @@ -87,7 +107,7 @@ func TestNewConfig(t *testing.T) {
{
name: "All empty fields = minimal valid configuration",
settings: `{
"chatid" :"chat-id",
"chatid" :"-1312",
"message" :"",
"parse_mode" :"",
"disable_notifications" : null
Expand All @@ -97,7 +117,7 @@ func TestNewConfig(t *testing.T) {
},
expectedConfig: Config{
BotToken: "test-token",
ChatID: "chat-id",
ChatID: "-1312",
Message: templates.DefaultMessageEmbed,
ParseMode: DefaultTelegramParseMode,
DisableWebPagePreview: false,
Expand Down Expand Up @@ -191,13 +211,38 @@ func TestNewConfig(t *testing.T) {
},
},
{
name: "should fail if message_thread_id is not an int",
name: "should be able to parse an int MessageThreadID",
settings: `{"chatid": -1312, "message_thread_id": 12345678}`,
secureSettings: map[string][]byte{
"bottoken": []byte("test-token"),
},
expectedConfig: Config{
BotToken: "test-token",
ChatID: "-1312",
MessageThreadID: "12345678",
Message: templates.DefaultMessageEmbed,
ParseMode: "HTML",
DisableWebPagePreview: false,
ProtectContent: false,
DisableNotifications: false,
},
},
{
name: "should fail if message_thread_id is not an int #1",
settings: `{"chatid": "12345678", "message_thread_id": "notanint"}`,
secureSettings: map[string][]byte{
"bottoken": []byte("test-token"),
},
expectedInitError: "message thread id must be an integer",
},
{
name: "should fail if message_thread_id is not an int #2",
settings: `{"chatid": "12345678", "message_thread_id": {}}`,
secureSettings: map[string][]byte{
"bottoken": []byte("test-token"),
},
expectedInitError: "message thread id must be either a string or an int",
},
{
name: "should fail if message_thread_id is not a valid int32",
settings: `{"chatid": "12345678", "message_thread_id": "21474836471"}`,
Expand All @@ -206,6 +251,14 @@ func TestNewConfig(t *testing.T) {
},
expectedInitError: "message thread id must be an int32",
},
{
name: "should fail if message_thread_id is not a valid int32",
settings: `{"chatid": -1312, "message_thread_id": 21474836471}`,
secureSettings: map[string][]byte{
"bottoken": []byte("test-token"),
},
expectedInitError: "message thread id must be an int32",
},
}

for _, c := range cases {
Expand Down

0 comments on commit 814d7af

Please sign in to comment.