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/teams): add teams notification #18916

Closed
wants to merge 6 commits into from
Closed
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
40 changes: 39 additions & 1 deletion http/swagger.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11259,6 +11259,7 @@ components:
- $ref: "#/components/schemas/PagerDutyNotificationRule"
- $ref: "#/components/schemas/HTTPNotificationRule"
- $ref: "#/components/schemas/TelegramNotificationRule"
- $ref: "#/components/schemas/TeamsNotificationRule"
discriminator:
propertyName: type
mapping:
Expand All @@ -11267,6 +11268,7 @@ components:
pagerduty: "#/components/schemas/PagerDutyNotificationRule"
http: "#/components/schemas/HTTPNotificationRule"
telegram: "#/components/schemas/TelegramNotificationRule"
teams: "#/components/schemas/TeamsNotificationRule"
NotificationRule:
allOf:
- $ref: "#/components/schemas/NotificationRuleDiscriminator"
Expand Down Expand Up @@ -11438,6 +11440,27 @@ components:
allOf:
- $ref: "#/components/schemas/NotificationRuleBase"
- $ref: "#/components/schemas/SlackNotificationRuleBase"
TeamsNotificationRule:
allOf:
- $ref: "#/components/schemas/NotificationRuleBase"
- $ref: "#/components/schemas/TeamsNotificationRuleBase"
TeamsNotificationRuleBase:
type: object
required: [type, title, messageTemplate]
properties:
type:
description: The discriminator between other types of notification rules is "teams".
type: string
enum: [teams]
title:
description: The message title as a flux interpolated string.
type: string
messageTemplate:
description: The message template as a flux interpolated string.
type: string
summary:
description: The message summary as a flux interpolated string.
type: string
SMTPNotificationRule:
allOf:
- $ref: "#/components/schemas/NotificationRuleBase"
Expand Down Expand Up @@ -11512,13 +11535,15 @@ components:
- $ref: "#/components/schemas/PagerDutyNotificationEndpoint"
- $ref: "#/components/schemas/HTTPNotificationEndpoint"
- $ref: "#/components/schemas/TelegramNotificationEndpoint"
- $ref: "#/components/schemas/TeamsNotificationEndpoint"
discriminator:
propertyName: type
mapping:
slack: "#/components/schemas/SlackNotificationEndpoint"
pagerduty: "#/components/schemas/PagerDutyNotificationEndpoint"
http: "#/components/schemas/HTTPNotificationEndpoint"
telegram: "#/components/schemas/TelegramNotificationEndpoint"
teams: "#/components/schemas/TeamsNotificationEndpoint"
NotificationEndpoint:
allOf:
- $ref: "#/components/schemas/NotificationEndpointDiscrimator"
Expand Down Expand Up @@ -11650,9 +11675,22 @@ components:
channel:
description: ID of the telegram channel, a chat_id in https://core.telegram.org/bots/api#sendmessage .
type: string
TeamsNotificationEndpoint:
type: object
allOf:
- $ref: "#/components/schemas/NotificationEndpointBase"
- type: object
required: [url]
properties:
url:
description: Teams incoming webhook URL, see https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using#setting-up-a-custom-incoming-webhook .
type: string
secretURLSuffix:
description: A secret suffix that is appended to teams incoming webhook URL.
type: string
NotificationEndpointType:
type: string
enum: ["slack", "pagerduty", "http", "telegram"]
enum: ["slack", "pagerduty", "http", "telegram", "teams"]
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 @@ -13,13 +13,15 @@ const (
PagerDutyType = "pagerduty"
HTTPType = "http"
TelegramType = "telegram"
TeamsType = "teams"
)

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{} },
TeamsType: func() influxdb.NotificationEndpoint { return &Teams{} },
}

// UnmarshalJSON will convert the bytes to notification endpoint.
Expand Down
113 changes: 113 additions & 0 deletions notification/endpoint/endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,24 @@ func TestValidEndpoint(t *testing.T) {
},
err: nil,
},
{
name: "empty teams url",
src: &endpoint.Teams{
Base: goodBase,
},
err: &influxdb.Error{
Code: influxdb.EInvalid,
Msg: "teams: empty URL",
},
},
{
name: "empty teams SecretURLSuffix",
src: &endpoint.Teams{
Base: goodBase,
URL: "http://localhost",
},
err: nil,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
Expand Down Expand Up @@ -288,6 +306,39 @@ func TestJSON(t *testing.T) {
Token: influxdb.SecretField{Key: "token-key-1"},
},
},
{
name: "teams with secretURLSuffix",
src: &endpoint.Teams{
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://outlook.office.com/webhook/",
SecretURLSuffix: influxdb.SecretField{Key: "token-key-1"},
},
},
{
name: "teams without secretURLSuffix",
src: &endpoint.Teams{
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://outlook.office.com/webhook/0acbc9c2-c262-11ea-b3de-0242ac130004",
glinton marked this conversation as resolved.
Show resolved Hide resolved
},
},
}
for _, c := range cases {
b, err := json.Marshal(c.src)
Expand Down Expand Up @@ -461,6 +512,42 @@ func TestBackFill(t *testing.T) {
},
},
},
{
name: "simple Teams",
src: &endpoint.Teams{
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://outlook.office.com/webhook/",
SecretURLSuffix: influxdb.SecretField{
Value: strPtr("token-value"),
},
},
target: &endpoint.Teams{
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://outlook.office.com/webhook/",
SecretURLSuffix: influxdb.SecretField{
Key: id1 + "-token",
Value: strPtr("token-value"),
},
},
},
}
for _, c := range cases {
c.src.BackfillSecretKeys()
Expand Down Expand Up @@ -588,6 +675,32 @@ func TestSecretFields(t *testing.T) {
},
},
},
{
name: "simple Teams",
src: &endpoint.Teams{
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://outlook.office.com/webhook/",
SecretURLSuffix: 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()
Expand Down
72 changes: 72 additions & 0 deletions notification/endpoint/teams.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package endpoint

import (
"encoding/json"

"github.com/influxdata/influxdb/v2"
)

var _ influxdb.NotificationEndpoint = &Teams{}

const teamsSecretSuffix = "-token"

// Teams is the notification endpoint config of Microdoft teams.
type Teams struct {
Base
// URL is the teams incoming webhook URL, see https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using#setting-up-a-custom-incoming-webhook ,
// for example: https://outlook.office.com/webhook/0acbc9c2-c262-11ea-b3de-0242ac130004
URL string `json:"url"`
// SecretURLSuffix is an optional secret suffix that is added to URL ,
// for example: 0acbc9c2-c262-11ea-b3de-0242ac130004 is the secret part that is added to https://outlook.office.com/webhook/
SecretURLSuffix influxdb.SecretField `json:"secretURLSuffix"`
}

// BackfillSecretKeys fill back the secret field key during the unmarshalling
// if value of that secret field is not nil.
func (s *Teams) BackfillSecretKeys() {
if s.SecretURLSuffix.Key == "" && s.SecretURLSuffix.Value != nil {
s.SecretURLSuffix.Key = s.idStr() + teamsSecretSuffix
}
}

// SecretFields return available secret fields.
func (s Teams) SecretFields() []influxdb.SecretField {
arr := []influxdb.SecretField{}
if s.SecretURLSuffix.Key != "" {
arr = append(arr, s.SecretURLSuffix)
}
return arr
}

// Valid returns error if some configuration is invalid
func (s Teams) Valid() error {
if err := s.Base.valid(); err != nil {
return err
}
if s.URL == "" {
return &influxdb.Error{
Code: influxdb.EInvalid,
Msg: "teams: empty URL",
}
}
return nil
}

type teamsAlias Teams

// MarshalJSON implement json.Marshaler interface.
func (s Teams) MarshalJSON() ([]byte, error) {
return json.Marshal(
struct {
teamsAlias
Type string `json:"type"`
}{
teamsAlias: teamsAlias(s),
Type: s.Type(),
})
}

// Type returns the type.
func (s Teams) Type() string {
return TeamsType
}
1 change: 1 addition & 0 deletions notification/rule/rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ var typeToRule = map[string](func() influxdb.NotificationRule){
"pagerduty": func() influxdb.NotificationRule { return &PagerDuty{} },
"http": func() influxdb.NotificationRule { return &HTTP{} },
"telegram": func() influxdb.NotificationRule { return &Telegram{} },
"teams": func() influxdb.NotificationRule { return &Teams{} },
}

// UnmarshalJSON will convert
Expand Down
36 changes: 36 additions & 0 deletions notification/rule/rule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,42 @@ func TestJSON(t *testing.T) {
MessageTemplate: "blah",
},
},
{
name: "simple teams",
src: &rule.Teams{
Base: rule.Base{
ID: influxTesting.MustIDBase16(id1),
OwnerID: influxTesting.MustIDBase16(id2),
Name: "name1",
OrgID: influxTesting.MustIDBase16(id3),
RunbookLink: "runbooklink1",
SleepUntil: &time3,
Every: mustDuration("1h"),
TagRules: []notification.TagRule{
{
Tag: influxdb.Tag{
Key: "k1",
Value: "v1",
},
Operator: influxdb.NotEqual,
},
{
Tag: influxdb.Tag{
Key: "k2",
Value: "v2",
},
Operator: influxdb.RegexEqual,
},
},
CRUDLog: influxdb.CRUDLog{
CreatedAt: timeGen1.Now(),
UpdatedAt: timeGen2.Now(),
},
},
Title: "my title",
MessageTemplate: "msg1",
},
},
}
for _, c := range cases {
b, err := json.Marshal(c.src)
Expand Down
Loading