mirror of
https://github.com/TwiN/gatus.git
synced 2024-11-25 01:13:40 +01:00
fix(alerting): Resolve GoogleChat issue with bad payload when condition has "
in it
Fixes #362
This commit is contained in:
parent
fa47a199e5
commit
967124eb43
@ -24,7 +24,7 @@ type Config struct {
|
|||||||
// Custom is the configuration for the custom alerting provider
|
// Custom is the configuration for the custom alerting provider
|
||||||
Custom *custom.AlertProvider `yaml:"custom,omitempty"`
|
Custom *custom.AlertProvider `yaml:"custom,omitempty"`
|
||||||
|
|
||||||
// googlechat is the configuration for the Google chat alerting provider
|
// GoogleChat is the configuration for the Google chat alerting provider
|
||||||
GoogleChat *googlechat.AlertProvider `yaml:"googlechat,omitempty"`
|
GoogleChat *googlechat.AlertProvider `yaml:"googlechat,omitempty"`
|
||||||
|
|
||||||
// Discord is the configuration for the discord alerting provider
|
// Discord is the configuration for the discord alerting provider
|
||||||
|
@ -2,6 +2,7 @@ package googlechat
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -52,7 +53,7 @@ func (provider *AlertProvider) IsValid() bool {
|
|||||||
|
|
||||||
// Send an alert using the provider
|
// Send an alert using the provider
|
||||||
func (provider *AlertProvider) Send(endpoint *core.Endpoint, alert *alert.Alert, result *core.Result, resolved bool) error {
|
func (provider *AlertProvider) Send(endpoint *core.Endpoint, alert *alert.Alert, result *core.Result, resolved bool) error {
|
||||||
buffer := bytes.NewBuffer([]byte(provider.buildRequestBody(endpoint, alert, result, resolved)))
|
buffer := bytes.NewBuffer(provider.buildRequestBody(endpoint, alert, result, resolved))
|
||||||
request, err := http.NewRequest(http.MethodPost, provider.getWebhookURLForGroup(endpoint.Group), buffer)
|
request, err := http.NewRequest(http.MethodPost, provider.getWebhookURLForGroup(endpoint.Group), buffer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -70,8 +71,50 @@ func (provider *AlertProvider) Send(endpoint *core.Endpoint, alert *alert.Alert,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Body struct {
|
||||||
|
Cards []Cards `json:"cards"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Cards struct {
|
||||||
|
Sections []Sections `json:"sections"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Sections struct {
|
||||||
|
Widgets []Widgets `json:"widgets"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Widgets struct {
|
||||||
|
KeyValue KeyValue `json:"keyValue,omitempty"`
|
||||||
|
Buttons []Buttons `json:"buttons,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type KeyValue struct {
|
||||||
|
TopLabel string `json:"topLabel"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
ContentMultiline string `json:"contentMultiline"`
|
||||||
|
BottomLabel string `json:"bottomLabel"`
|
||||||
|
Icon string `json:"icon"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Buttons struct {
|
||||||
|
TextButton TextButton `json:"textButton"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TextButton struct {
|
||||||
|
Text string `json:"text"`
|
||||||
|
OnClick OnClick `json:"onClick"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type OnClick struct {
|
||||||
|
OpenLink OpenLink `json:"openLink"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type OpenLink struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
// buildRequestBody builds the request body for the provider
|
// buildRequestBody builds the request body for the provider
|
||||||
func (provider *AlertProvider) buildRequestBody(endpoint *core.Endpoint, alert *alert.Alert, result *core.Result, resolved bool) string {
|
func (provider *AlertProvider) buildRequestBody(endpoint *core.Endpoint, alert *alert.Alert, result *core.Result, resolved bool) []byte {
|
||||||
var message, color string
|
var message, color string
|
||||||
if resolved {
|
if resolved {
|
||||||
color = "#36A64F"
|
color = "#36A64F"
|
||||||
@ -94,49 +137,90 @@ func (provider *AlertProvider) buildRequestBody(endpoint *core.Endpoint, alert *
|
|||||||
if alertDescription := alert.GetDescription(); len(alertDescription) > 0 {
|
if alertDescription := alert.GetDescription(); len(alertDescription) > 0 {
|
||||||
description = ":: " + alertDescription
|
description = ":: " + alertDescription
|
||||||
}
|
}
|
||||||
return fmt.Sprintf(`{
|
body, _ := json.Marshal(Body{
|
||||||
"cards": [
|
Cards: []Cards{
|
||||||
{
|
{
|
||||||
"sections": [
|
Sections: []Sections{
|
||||||
{
|
{
|
||||||
"widgets": [
|
Widgets: []Widgets{
|
||||||
{
|
{
|
||||||
"keyValue": {
|
KeyValue: KeyValue{
|
||||||
"topLabel": "%s [%s]",
|
TopLabel: endpoint.DisplayName(),
|
||||||
"content": "%s",
|
Content: message,
|
||||||
"contentMultiline": "true",
|
ContentMultiline: "true",
|
||||||
"bottomLabel": "%s",
|
BottomLabel: description,
|
||||||
"icon": "BOOKMARK"
|
Icon: "BOOKMARK",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"keyValue": {
|
KeyValue: KeyValue{
|
||||||
"topLabel": "Condition results",
|
TopLabel: "Condition results",
|
||||||
"content": "%s",
|
Content: results,
|
||||||
"contentMultiline": "true",
|
ContentMultiline: "true",
|
||||||
"icon": "DESCRIPTION"
|
Icon: "DESCRIPTION",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"buttons": [
|
Buttons: []Buttons{
|
||||||
{
|
{
|
||||||
"textButton": {
|
TextButton: TextButton{
|
||||||
"text": "URL",
|
Text: "Open",
|
||||||
"onClick": {
|
OnClick: OnClick{OpenLink: OpenLink{URL: endpoint.URL}},
|
||||||
"openLink": {
|
},
|
||||||
"url": "%s"
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
]
|
},
|
||||||
}
|
},
|
||||||
]
|
},
|
||||||
}
|
})
|
||||||
]
|
return body
|
||||||
}
|
|
||||||
]
|
// return fmt.Sprintf(`{
|
||||||
}`, endpoint.Name, endpoint.Group, message, description, results, endpoint.URL)
|
// "cards": [
|
||||||
|
// {
|
||||||
|
// "sections": [
|
||||||
|
// {
|
||||||
|
// "widgets": [
|
||||||
|
// {
|
||||||
|
// "keyValue": {
|
||||||
|
// "topLabel": "%s [%s]",
|
||||||
|
// "content": "%s",
|
||||||
|
// "contentMultiline": "true",
|
||||||
|
// "bottomLabel": "%s",
|
||||||
|
// "icon": "BOOKMARK"
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// "keyValue": {
|
||||||
|
// "topLabel": "Condition results",
|
||||||
|
// "content": "%s",
|
||||||
|
// "contentMultiline": "true",
|
||||||
|
// "icon": "DESCRIPTION"
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// "buttons": [
|
||||||
|
// {
|
||||||
|
// "textButton": {
|
||||||
|
// "text": "URL",
|
||||||
|
// "onClick": {
|
||||||
|
// "openLink": {
|
||||||
|
// "url": "%s"
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
// }
|
||||||
|
//]
|
||||||
|
//}`, endpoint.Name, endpoint.Group, message, description, results, endpoint.URL)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getWebhookURLForGroup returns the appropriate Webhook URL integration to for a given group
|
// getWebhookURLForGroup returns the appropriate Webhook URL integration to for a given group
|
||||||
|
@ -151,20 +151,20 @@ func TestAlertProvider_buildRequestBody(t *testing.T) {
|
|||||||
Provider: AlertProvider{},
|
Provider: AlertProvider{},
|
||||||
Alert: alert.Alert{Description: &firstDescription, SuccessThreshold: 5, FailureThreshold: 3},
|
Alert: alert.Alert{Description: &firstDescription, SuccessThreshold: 5, FailureThreshold: 3},
|
||||||
Resolved: false,
|
Resolved: false,
|
||||||
ExpectedBody: "{\n \"cards\": [\n {\n \"sections\": [\n {\n \"widgets\": [\n {\n \"keyValue\": {\n \"topLabel\": \"endpoint-name []\",\n \"content\": \"\u003cfont color='#DD0000'\u003eAn alert has been triggered due to having failed 3 time(s) in a row\u003c/font\u003e\",\n \"contentMultiline\": \"true\",\n \"bottomLabel\": \":: description-1\",\n \"icon\": \"BOOKMARK\"\n }\n },\n {\n \"keyValue\": {\n \"topLabel\": \"Condition results\",\n \"content\": \"❌ [CONNECTED] == true\u003cbr\u003e❌ [STATUS] == 200\u003cbr\u003e\",\n \"contentMultiline\": \"true\",\n \"icon\": \"DESCRIPTION\"\n }\n },\n {\n \"buttons\": [\n {\n \"textButton\": {\n \"text\": \"URL\",\n \"onClick\": {\n \"openLink\": {\n \"url\": \"\"\n }\n }\n }\n }\n ]\n }\n ]\n }\n ]\n }\n]\n}",
|
ExpectedBody: `{"cards":[{"sections":[{"widgets":[{"keyValue":{"topLabel":"endpoint-name","content":"\u003cfont color='#DD0000'\u003eAn alert has been triggered due to having failed 3 time(s) in a row\u003c/font\u003e","contentMultiline":"true","bottomLabel":":: description-1","icon":"BOOKMARK"}},{"keyValue":{"topLabel":"Condition results","content":"❌ [CONNECTED] == true\u003cbr\u003e❌ [STATUS] == 200\u003cbr\u003e","contentMultiline":"true","bottomLabel":"","icon":"DESCRIPTION"}},{"keyValue":{"topLabel":"","content":"","contentMultiline":"","bottomLabel":"","icon":""},"buttons":[{"textButton":{"text":"Open","onClick":{"openLink":{"url":"https://example.org"}}}}]}]}]}]}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "resolved",
|
Name: "resolved",
|
||||||
Provider: AlertProvider{},
|
Provider: AlertProvider{},
|
||||||
Alert: alert.Alert{Description: &secondDescription, SuccessThreshold: 5, FailureThreshold: 3},
|
Alert: alert.Alert{Description: &secondDescription, SuccessThreshold: 5, FailureThreshold: 3},
|
||||||
Resolved: true,
|
Resolved: true,
|
||||||
ExpectedBody: "{\n \"cards\": [\n {\n \"sections\": [\n {\n \"widgets\": [\n {\n \"keyValue\": {\n \"topLabel\": \"endpoint-name []\",\n \"content\": \"\u003cfont color='#36A64F'\u003eAn alert has been resolved after passing successfully 5 time(s) in a row\u003c/font\u003e\",\n \"contentMultiline\": \"true\",\n \"bottomLabel\": \":: description-2\",\n \"icon\": \"BOOKMARK\"\n }\n },\n {\n \"keyValue\": {\n \"topLabel\": \"Condition results\",\n \"content\": \"✅ [CONNECTED] == true\u003cbr\u003e✅ [STATUS] == 200\u003cbr\u003e\",\n \"contentMultiline\": \"true\",\n \"icon\": \"DESCRIPTION\"\n }\n },\n {\n \"buttons\": [\n {\n \"textButton\": {\n \"text\": \"URL\",\n \"onClick\": {\n \"openLink\": {\n \"url\": \"\"\n }\n }\n }\n }\n ]\n }\n ]\n }\n ]\n }\n]\n}",
|
ExpectedBody: `{"cards":[{"sections":[{"widgets":[{"keyValue":{"topLabel":"endpoint-name","content":"\u003cfont color='#36A64F'\u003eAn alert has been resolved after passing successfully 5 time(s) in a row\u003c/font\u003e","contentMultiline":"true","bottomLabel":":: description-2","icon":"BOOKMARK"}},{"keyValue":{"topLabel":"Condition results","content":"✅ [CONNECTED] == true\u003cbr\u003e✅ [STATUS] == 200\u003cbr\u003e","contentMultiline":"true","bottomLabel":"","icon":"DESCRIPTION"}},{"keyValue":{"topLabel":"","content":"","contentMultiline":"","bottomLabel":"","icon":""},"buttons":[{"textButton":{"text":"Open","onClick":{"openLink":{"url":"https://example.org"}}}}]}]}]}]}`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, scenario := range scenarios {
|
for _, scenario := range scenarios {
|
||||||
t.Run(scenario.Name, func(t *testing.T) {
|
t.Run(scenario.Name, func(t *testing.T) {
|
||||||
body := scenario.Provider.buildRequestBody(
|
body := scenario.Provider.buildRequestBody(
|
||||||
&core.Endpoint{Name: "endpoint-name"},
|
&core.Endpoint{Name: "endpoint-name", URL: "https://example.org"},
|
||||||
&scenario.Alert,
|
&scenario.Alert,
|
||||||
&core.Result{
|
&core.Result{
|
||||||
ConditionResults: []*core.ConditionResult{
|
ConditionResults: []*core.ConditionResult{
|
||||||
@ -174,13 +174,13 @@ func TestAlertProvider_buildRequestBody(t *testing.T) {
|
|||||||
},
|
},
|
||||||
scenario.Resolved,
|
scenario.Resolved,
|
||||||
)
|
)
|
||||||
b, _ := json.Marshal(body)
|
//b, _ := json.Marshal(body)
|
||||||
e, _ := json.Marshal(scenario.ExpectedBody)
|
//e, _ := json.Marshal(scenario.ExpectedBody)
|
||||||
if body != scenario.ExpectedBody {
|
if string(body) != scenario.ExpectedBody {
|
||||||
t.Errorf("expected:\n%s\ngot:\n%s", e, b)
|
t.Errorf("expected:\n%s\ngot:\n%s", scenario.ExpectedBody, body)
|
||||||
}
|
}
|
||||||
out := make(map[string]interface{})
|
out := make(map[string]interface{})
|
||||||
if err := json.Unmarshal([]byte(body), &out); err != nil {
|
if err := json.Unmarshal(body, &out); err != nil {
|
||||||
t.Error("expected body to be valid JSON, got error:", err.Error())
|
t.Error("expected body to be valid JSON, got error:", err.Error())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user