Skip to content

Commit

Permalink
Add card feature to msteamsv2_config
Browse files Browse the repository at this point in the history
Signed-off-by: jverger <[email protected]>
  • Loading branch information
jverger committed Feb 3, 2025
1 parent b5d1a64 commit ecde9eb
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 33 deletions.
1 change: 1 addition & 0 deletions config/notifiers.go
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,7 @@ type MSTeamsV2Config struct {

Title string `yaml:"title,omitempty" json:"title,omitempty"`
Text string `yaml:"text,omitempty" json:"text,omitempty"`
Card string `yaml:"card,omitempty" json:"card,omitempty"`
}

func (c *MSTeamsV2Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
Expand Down
5 changes: 5 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -992,6 +992,11 @@ Microsoft Teams v2 notifications using the new message format with adaptive card
# Message body template.
[ text: <tmpl_string> | default = '{{ template "msteamsv2.default.text" . }}' ]
# Message body card.
# If not null, it will override title and text values (no need to configure these values)
# You can find a complete sample file template here 'examples/msteamsv2/card.tmpl' and provide '{{ template "msteams.card" . }}' in the card value to test.
[ card: <tmpl_string> ]
# The HTTP client's configuration.
[ http_config: <http_config> | default = global.http_config ]
```
Expand Down
75 changes: 75 additions & 0 deletions examples/msteamsv2/card.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
{{ define "msteams.card" }}
{
"Type": "message",
"Attachments": [
{
"contentType": "application/vnd.microsoft.card.adaptive",
"content": {
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.4",
"msteams": {
"width": "Full"
},
"body": [
{
"type": "ColumnSet",
"style": "{{ if eq .Status "firing" }}attention{{ else if eq .Status "resolved" }}good{{ else }}warning{{ end }}",
"columns": [
{
"type": "Column",
"width": "stretch",
"items": [
{
"type": "TextBlock",
"weight": "Bolder",
"size": "ExtraLarge",
"color": "{{ if eq .Status "firing" }}attention{{ else if eq .Status "resolved" }}good{{ else }}warning{{ end }}",
"text": "{{ if eq .Status "firing" }}🔥{{ else if eq .Status "resolved" }}✅{{ else }}⚠️{{ end }} Prometheus alert {{ if eq .Status "resolved" }}(Resolved){{ else if eq .Status "firing" }}(Firing){{ else if eq .Status "unknown" }}(Unknown){{ else }}(Warning){{ end }}"
},
{
"type": "TextBlock",
"weight": "Bolder",
"size": "ExtraLarge",
"text": "{{ if eq .Status "resolved" }}(Resolved) {{ end }}{{ .CommonAnnotations.summary }}",
"wrap": true
}
]
}
]
},
{
"type": "FactSet",
"facts": [
{ "title": "Status", "value": "{{ .Status }} {{ if eq .Status "firing" }}🔥{{ else if eq .Status "resolved" }}✅{{ else }}⚠️{{ end }}" }
{{ if .CommonLabels.alertname }}, { "title": "Alert", "value": "{{ .CommonLabels.alertname }}" }{{ end }}
{{ if .CommonLabels.instance }}, { "title": "In host", "value": "{{ .CommonLabels.instance }}" }{{ end }}
{{ if .CommonLabels.severity }}, { "title": "Severity", "value": "{{ .CommonLabels.severity }} {{ if eq .CommonLabels.severity "critical" }}❌{{ else if eq .CommonLabels.severity "error" }}❗️{{ else if eq .CommonLabels.severity "warning" }}⚠️{{ else if eq .CommonLabels.severity "info" }}ℹ️{{ else }}❓{{ end }}" }{{ end }}
{{ if .CommonAnnotations.description }}, { "title": "Description", "value": "{{ .CommonAnnotations.description }}" }{{ end }}
{{- range $key, $value := .CommonLabels }}
{{- if and (ne $key "alertname") (ne $key "instance") (ne $key "severity") }}
, { "title": "{{ $key }}", "value": "{{ $value }}" }
{{- end }}
{{- end }}
{{- range $key, $value := .CommonAnnotations }}
{{- if and (ne $key "summary") (ne $key "description") }}
, { "title": "{{ $key }}", "value": "{{ $value }}" }
{{- end }}
{{- end }}
]
}
]
{{ if .CommonAnnotations.runbook_url }},
"actions": [
{
"type": "Action.OpenUrl",
"title": "View details",
"url": "{{ .CommonAnnotations.runbook_url }}"
}
]
{{ end }}
}
}
]
}
{{ end }}
81 changes: 48 additions & 33 deletions notify/msteamsv2/msteamsv2.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error)
if err != nil {
return false, err
}
card := tmpl(n.conf.Card)
if err != nil {
return false, err
}

alerts := types.Alerts(as...)
color := colorGrey
Expand All @@ -146,44 +150,55 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error)
url = strings.TrimSpace(string(content))
}

// A message as referenced in https://learn.microsoft.com/en-us/connectors/teams/?tabs=text1%2Cdotnet#request-body-schema
t := teamsMessage{
Type: "message",
Attachments: []Attachment{
{
ContentType: "application/vnd.microsoft.card.adaptive",
ContentURL: nil,
Content: Content{
Schema: "http://adaptivecards.io/schemas/adaptive-card.json",
Type: "AdaptiveCard",
Version: "1.2",
Body: []Body{
{
Type: "TextBlock",
Text: title,
Weight: "Bolder",
Size: "Medium",
Wrap: true,
Style: "heading",
Color: color,
// If the card is empty, use title and text otherwise use card.
var payload bytes.Buffer
if card == "" {
// A message as referenced in https://learn.microsoft.com/en-us/connectors/teams/?tabs=text1%2Cdotnet#request-body-schema
t := teamsMessage{
Type: "message",
Attachments: []Attachment{
{
ContentType: "application/vnd.microsoft.card.adaptive",
ContentURL: nil,
Content: Content{
Schema: "http://adaptivecards.io/schemas/adaptive-card.json",
Type: "AdaptiveCard",
Version: "1.2",
Body: []Body{
{
Type: "TextBlock",
Text: title,
Weight: "Bolder",
Size: "Medium",
Wrap: true,
Style: "heading",
Color: color,
},
{
Type: "TextBlock",
Text: text,
},
},
{
Type: "TextBlock",
Text: text,
Wrap: true,
Msteams: Msteams{
Width: "full",
},
},
Msteams: Msteams{
Width: "full",
},
},
},
},
}

var payload bytes.Buffer
if err = json.NewEncoder(&payload).Encode(t); err != nil {
return false, err
}

if err = json.NewEncoder(&payload).Encode(t); err != nil {
return false, err
}
} else {
// Transform card string into object
var jsonMap map[string]interface{}
json.Unmarshal([]byte(card), &jsonMap)
n.logger.Debug("jsonMap", "jsonMap", jsonMap)

if err = json.NewEncoder(&payload).Encode(jsonMap); err != nil {
return false, err
}
}

resp, err := n.postJSONFunc(ctx, n.client, url, &payload)
Expand Down

0 comments on commit ecde9eb

Please sign in to comment.