Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(notification/telegram): add telegram notification #18218

Merged
merged 9 commits into from
Jul 25, 2020
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@

### Features

1. [19075](https://github.com/influxdata/influxdb/pull/19075): Aadd resource links to a stack's resources from public HTTP API list/read calls
1. [19075](https://github.com/influxdata/influxdb/pull/19075): Add resource links to a stack's resources from public HTTP API list/read calls
1. [18218](https://github.com/influxdata/influxdb/pull/18218): Add Telegram notification.

### Bug Fixes

Expand Down
44 changes: 43 additions & 1 deletion http/swagger.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11391,13 +11391,15 @@ components:
- $ref: "#/components/schemas/SMTPNotificationRule"
- $ref: "#/components/schemas/PagerDutyNotificationRule"
- $ref: "#/components/schemas/HTTPNotificationRule"
- $ref: "#/components/schemas/TelegramNotificationRule"
discriminator:
propertyName: type
mapping:
slack: "#/components/schemas/SlackNotificationRule"
smtp: "#/components/schemas/SMTPNotificationRule"
pagerduty: "#/components/schemas/PagerDutyNotificationRule"
http: "#/components/schemas/HTTPNotificationRule"
telegram: "#/components/schemas/TelegramNotificationRule"
NotificationRule:
allOf:
- $ref: "#/components/schemas/NotificationRuleDiscriminator"
Expand Down Expand Up @@ -11599,6 +11601,31 @@ components:
enum: [pagerduty]
messageTemplate:
type: string
TelegramNotificationRule:
allOf:
- $ref: "#/components/schemas/NotificationRuleBase"
- $ref: "#/components/schemas/TelegramNotificationRuleBase"
TelegramNotificationRuleBase:
type: object
required: [type, messageTemplate, channel]
properties:
type:
description: The discriminator between other types of notification rules is "telegram".
type: string
enum: [telegram]
messageTemplate:
description: The message template as a flux interpolated string.
type: string
parseMode:
description: Parse mode of the message text per https://core.telegram.org/bots/api#formatting-options . Defaults to "MarkdownV2" .
type: string
enum:
- MarkdownV2
- HTML
- Markdown
disableWebPagePreview:
description: Disables preview of web links in the sent messages when "true". Defaults to "false" .
type: boolean
NotificationEndpointUpdate:
type: object

Expand All @@ -11617,12 +11644,14 @@ components:
- $ref: "#/components/schemas/SlackNotificationEndpoint"
- $ref: "#/components/schemas/PagerDutyNotificationEndpoint"
- $ref: "#/components/schemas/HTTPNotificationEndpoint"
- $ref: "#/components/schemas/TelegramNotificationEndpoint"
discriminator:
propertyName: type
mapping:
slack: "#/components/schemas/SlackNotificationEndpoint"
pagerduty: "#/components/schemas/PagerDutyNotificationEndpoint"
http: "#/components/schemas/HTTPNotificationEndpoint"
telegram: "#/components/schemas/TelegramNotificationEndpoint"
NotificationEndpoint:
allOf:
- $ref: "#/components/schemas/NotificationEndpointDiscrimator"
Expand Down Expand Up @@ -11741,9 +11770,22 @@ components:
description: Customized headers.
additionalProperties:
type: string
TelegramNotificationEndpoint:
type: object
allOf:
- $ref: "#/components/schemas/NotificationEndpointBase"
- type: object
required: [token, channel]
properties:
token:
description: Specifies the Telegram bot token. See https://core.telegram.org/bots#creating-a-new-bot .
type: string
channel:
description: ID of the telegram channel, a chat_id in https://core.telegram.org/bots/api#sendmessage .
type: string
NotificationEndpointType:
type: string
enum: ["slack", "pagerduty", "http"]
enum: ["slack", "pagerduty", "http", "telegram"]
DBRP:
required:
- orgID
Expand Down
2 changes: 2 additions & 0 deletions notification/endpoint/endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ const (
SlackType = "slack"
PagerDutyType = "pagerduty"
HTTPType = "http"
TelegramType = "telegram"
)

var typeToEndpoint = map[string]func() influxdb.NotificationEndpoint{
SlackType: func() influxdb.NotificationEndpoint { return &Slack{} },
PagerDutyType: func() influxdb.NotificationEndpoint { return &PagerDuty{} },
HTTPType: func() influxdb.NotificationEndpoint { return &HTTP{} },
TelegramType: func() influxdb.NotificationEndpoint { return &Telegram{} },
}

// UnmarshalJSON will convert the bytes to notification endpoint.
Expand Down
225 changes: 224 additions & 1 deletion notification/endpoint/endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func TestValidEndpoint(t *testing.T) {
},
},
{
name: "empty name",
name: "empty name PagerDuty",
src: &endpoint.PagerDuty{
Base: endpoint.Base{
ID: influxTesting.MustIDBase16Ptr(id1),
Expand All @@ -70,6 +70,22 @@ func TestValidEndpoint(t *testing.T) {
Msg: "Notification Endpoint Name can't be empty",
},
},
{
name: "empty name Telegram",
src: &endpoint.Telegram{
Base: endpoint.Base{
ID: influxTesting.MustIDBase16Ptr(id1),
OrgID: influxTesting.MustIDBase16Ptr(id3),
Status: influxdb.Active,
},
Token: influxdb.SecretField{Key: id1 + "-token"},
Channel: "-1001406363649",
},
err: &influxdb.Error{
Code: influxdb.EInvalid,
Msg: "Notification Endpoint Name can't be empty",
},
},
{
name: "empty slack url",
src: &endpoint.Slack{
Expand Down Expand Up @@ -136,6 +152,36 @@ func TestValidEndpoint(t *testing.T) {
Msg: "invalid http username/password for basic auth",
},
},
{
name: "empty telegram token",
src: &endpoint.Telegram{
Base: goodBase,
},
err: &influxdb.Error{
Code: influxdb.EInvalid,
Msg: "empty telegram bot token",
},
},
{
name: "empty telegram channel",
src: &endpoint.Telegram{
Base: goodBase,
Token: influxdb.SecretField{Key: id1 + "-token"},
},
err: &influxdb.Error{
Code: influxdb.EInvalid,
Msg: "empty telegram channel",
},
},
{
name: "valid telegram token",
src: &endpoint.Telegram{
Base: goodBase,
Token: influxdb.SecretField{Key: id1 + "-token"},
Channel: "-1001406363649",
},
err: nil,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
Expand Down Expand Up @@ -226,6 +272,22 @@ func TestJSON(t *testing.T) {
Password: influxdb.SecretField{Key: "password-key"},
},
},
{
name: "simple Telegram",
src: &endpoint.Telegram{
Base: endpoint.Base{
ID: influxTesting.MustIDBase16Ptr(id1),
Name: "nameTelegram",
OrgID: influxTesting.MustIDBase16Ptr(id3),
Status: influxdb.Active,
CRUDLog: influxdb.CRUDLog{
CreatedAt: timeGen1.Now(),
UpdatedAt: timeGen2.Now(),
},
},
Token: influxdb.SecretField{Key: "token-key-1"},
},
},
}
for _, c := range cases {
b, err := json.Marshal(c.src)
Expand Down Expand Up @@ -365,6 +427,40 @@ func TestBackFill(t *testing.T) {
},
},
},
{
name: "simple Telegram",
src: &endpoint.Telegram{
Base: endpoint.Base{
ID: influxTesting.MustIDBase16Ptr(id1),
Name: "name1",
OrgID: influxTesting.MustIDBase16Ptr(id3),
Status: influxdb.Active,
CRUDLog: influxdb.CRUDLog{
CreatedAt: timeGen1.Now(),
UpdatedAt: timeGen2.Now(),
},
},
Token: influxdb.SecretField{
Value: strPtr("token-value"),
},
},
target: &endpoint.Telegram{
Base: endpoint.Base{
ID: influxTesting.MustIDBase16Ptr(id1),
Name: "name1",
OrgID: influxTesting.MustIDBase16Ptr(id3),
Status: influxdb.Active,
CRUDLog: influxdb.CRUDLog{
CreatedAt: timeGen1.Now(),
UpdatedAt: timeGen2.Now(),
},
},
Token: influxdb.SecretField{
Key: id1 + "-token",
Value: strPtr("token-value"),
},
},
},
}
for _, c := range cases {
c.src.BackfillSecretKeys()
Expand All @@ -374,6 +470,133 @@ func TestBackFill(t *testing.T) {
}
}

func TestSecretFields(t *testing.T) {
cases := []struct {
name string
src influxdb.NotificationEndpoint
secrets []influxdb.SecretField
}{
{
name: "simple Slack",
src: &endpoint.Slack{
Base: endpoint.Base{
ID: influxTesting.MustIDBase16Ptr(id1),
Name: "name1",
OrgID: influxTesting.MustIDBase16Ptr(id3),
Status: influxdb.Active,
CRUDLog: influxdb.CRUDLog{
CreatedAt: timeGen1.Now(),
UpdatedAt: timeGen2.Now(),
},
},
URL: "https://slack.com/api/chat.postMessage",
Token: influxdb.SecretField{
Key: id1 + "-token",
Value: strPtr("token-value"),
},
},
secrets: []influxdb.SecretField{
{
Key: id1 + "-token",
Value: strPtr("token-value"),
},
},
},
{
name: "simple pagerduty",
src: &endpoint.PagerDuty{
Base: endpoint.Base{
ID: influxTesting.MustIDBase16Ptr(id1),
Name: "name1",
OrgID: influxTesting.MustIDBase16Ptr(id3),
Status: influxdb.Active,
CRUDLog: influxdb.CRUDLog{
CreatedAt: timeGen1.Now(),
UpdatedAt: timeGen2.Now(),
},
},
ClientURL: "https://events.pagerduty.com/v2/enqueue",
RoutingKey: influxdb.SecretField{
Key: id1 + "-routing-key",
Value: strPtr("routing-key-value"),
},
},
secrets: []influxdb.SecretField{
{
Key: id1 + "-routing-key",
Value: strPtr("routing-key-value"),
},
},
},
{
name: "http with user and password",
src: &endpoint.HTTP{
Base: endpoint.Base{
ID: influxTesting.MustIDBase16Ptr(id1),
Name: "name1",
OrgID: influxTesting.MustIDBase16Ptr(id3),
Status: influxdb.Active,
CRUDLog: influxdb.CRUDLog{
CreatedAt: timeGen1.Now(),
UpdatedAt: timeGen2.Now(),
},
},
AuthMethod: "basic",
URL: "http://example.com",
Username: influxdb.SecretField{
Key: id1 + "-username",
Value: strPtr("user1"),
},
Password: influxdb.SecretField{
Key: id1 + "-password",
Value: strPtr("password1"),
},
},
secrets: []influxdb.SecretField{
{
Key: id1 + "-username",
Value: strPtr("user1"),
},
{
Key: id1 + "-password",
Value: strPtr("password1"),
},
},
},
{
name: "simple Telegram",
src: &endpoint.Telegram{
Base: endpoint.Base{
ID: influxTesting.MustIDBase16Ptr(id1),
Name: "name1",
OrgID: influxTesting.MustIDBase16Ptr(id3),
Status: influxdb.Active,
CRUDLog: influxdb.CRUDLog{
CreatedAt: timeGen1.Now(),
UpdatedAt: timeGen2.Now(),
},
},
Token: influxdb.SecretField{
Key: id1 + "-token",
Value: strPtr("token-value"),
},
},
secrets: []influxdb.SecretField{
{
Key: id1 + "-token",
Value: strPtr("token-value"),
},
},
},
}
for _, c := range cases {
secretFields := c.src.SecretFields()
if diff := cmp.Diff(c.secrets, secretFields); diff != "" {
t.Errorf("failed %s, NotificationEndpoint are different -got/+want\ndiff %s", c.name, diff)
}
}
}

func strPtr(s string) *string {
ss := new(string)
*ss = s
Expand Down
Loading