Skip to content

Commit 129fb82

Browse files
aalmenarTwiN
andauthored
feat(alerting): Add RESULT_CONDITIONS in custom alert to have more information (#1086)
feat(alerting): Add RESULT_CONDITIONS in custom alert to have more information on an alert while using custom alerting module Add testing of new feature Co-authored-by: TwiN <[email protected]>
1 parent 374be99 commit 129fb82

File tree

3 files changed

+84
-1
lines changed

3 files changed

+84
-1
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2450,7 +2450,8 @@ Furthermore, you may use the following placeholders in the body (`alerting.custo
24502450
- `[ENDPOINT_GROUP]` (resolved from `endpoints[].group`)
24512451
- `[ENDPOINT_URL]` (resolved from `endpoints[].url`)
24522452
- `[RESULT_ERRORS]` (resolved from the health evaluation of a given health check)
2453-
2453+
- `[RESULT_CONDITIONS]` (condition results from the health evaluation of a given health check)
2454+
-
24542455
If you have an alert using the `custom` provider with `send-on-resolved` set to `true`, you can use the
24552456
`[ALERT_TRIGGERED_OR_RESOLVED]` placeholder to differentiate the notifications.
24562457
The aforementioned placeholder will be replaced by `TRIGGERED` or `RESOLVED` accordingly, though it can be modified

alerting/provider/custom/custom.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,25 @@ func (provider *AlertProvider) buildHTTPRequest(cfg *Config, ep *endpoint.Endpoi
111111
resultErrors := strings.ReplaceAll(strings.Join(result.Errors, ","), "\"", "\\\"")
112112
body = strings.ReplaceAll(body, "[RESULT_ERRORS]", resultErrors)
113113
url = strings.ReplaceAll(url, "[RESULT_ERRORS]", resultErrors)
114+
115+
if len(result.ConditionResults) > 0 && strings.Contains(body, "[RESULT_CONDITIONS]") {
116+
var formattedConditionResults string
117+
for index, conditionResult := range result.ConditionResults {
118+
var prefix string
119+
if conditionResult.Success {
120+
prefix = "✅"
121+
} else {
122+
prefix = "❌"
123+
}
124+
formattedConditionResults += fmt.Sprintf("%s - `%s`", prefix, conditionResult.Condition)
125+
if index < len(result.ConditionResults)-1 {
126+
formattedConditionResults += ", "
127+
}
128+
}
129+
body = strings.ReplaceAll(body, "[RESULT_CONDITIONS]", formattedConditionResults)
130+
url = strings.ReplaceAll(url, "[RESULT_CONDITIONS]", formattedConditionResults)
131+
}
132+
114133
if resolved {
115134
body = strings.ReplaceAll(body, "[ALERT_TRIGGERED_OR_RESOLVED]", provider.GetAlertStatePlaceholderValue(cfg, true))
116135
url = strings.ReplaceAll(url, "[ALERT_TRIGGERED_OR_RESOLVED]", provider.GetAlertStatePlaceholderValue(cfg, true))

alerting/provider/custom/custom_test.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,69 @@ func TestAlertProvider_buildHTTPRequestWithCustomPlaceholder(t *testing.T) {
261261
}
262262
}
263263

264+
func TestAlertProvider_buildHTTPRequestWithCustomPlaceholderAndResultConditions(t *testing.T) {
265+
alertProvider := &AlertProvider{
266+
DefaultConfig: Config{
267+
URL: "https://example.com/[ENDPOINT_GROUP]/[ENDPOINT_NAME]?event=[ALERT_TRIGGERED_OR_RESOLVED]&description=[ALERT_DESCRIPTION]",
268+
Body: "[ENDPOINT_NAME],[ENDPOINT_GROUP],[ALERT_DESCRIPTION],[ALERT_TRIGGERED_OR_RESOLVED],[RESULT_CONDITIONS]",
269+
Headers: nil,
270+
Placeholders: map[string]map[string]string{
271+
"ALERT_TRIGGERED_OR_RESOLVED": {
272+
"RESOLVED": "fixed",
273+
"TRIGGERED": "boom",
274+
},
275+
},
276+
},
277+
}
278+
alertDescription := "alert-description"
279+
scenarios := []struct {
280+
AlertProvider *AlertProvider
281+
Resolved bool
282+
ExpectedURL string
283+
ExpectedBody string
284+
NoConditions bool
285+
}{
286+
{
287+
AlertProvider: alertProvider,
288+
Resolved: true,
289+
ExpectedURL: "https://example.com/endpoint-group/endpoint-name?event=fixed&description=alert-description",
290+
ExpectedBody: "endpoint-name,endpoint-group,alert-description,fixed,✅ - `[CONNECTED] == true`, ✅ - `[STATUS] == 200`",
291+
},
292+
{
293+
AlertProvider: alertProvider,
294+
Resolved: false,
295+
ExpectedURL: "https://example.com/endpoint-group/endpoint-name?event=boom&description=alert-description",
296+
ExpectedBody: "endpoint-name,endpoint-group,alert-description,boom,❌ - `[CONNECTED] == true`, ❌ - `[STATUS] == 200`",
297+
},
298+
}
299+
for _, scenario := range scenarios {
300+
t.Run(fmt.Sprintf("resolved-%v-with-custom-placeholders", scenario.Resolved), func(t *testing.T) {
301+
var conditionResults []*endpoint.ConditionResult
302+
if !scenario.NoConditions {
303+
conditionResults = []*endpoint.ConditionResult{
304+
{Condition: "[CONNECTED] == true", Success: scenario.Resolved},
305+
{Condition: "[STATUS] == 200", Success: scenario.Resolved},
306+
}
307+
}
308+
309+
request := alertProvider.buildHTTPRequest(
310+
&alertProvider.DefaultConfig,
311+
&endpoint.Endpoint{Name: "endpoint-name", Group: "endpoint-group"},
312+
&alert.Alert{Description: &alertDescription},
313+
&endpoint.Result{ConditionResults: conditionResults},
314+
scenario.Resolved,
315+
)
316+
if request.URL.String() != scenario.ExpectedURL {
317+
t.Error("expected URL to be", scenario.ExpectedURL, "got", request.URL.String())
318+
}
319+
body, _ := io.ReadAll(request.Body)
320+
if string(body) != scenario.ExpectedBody {
321+
t.Error("expected body to be", scenario.ExpectedBody, "got", string(body))
322+
}
323+
})
324+
}
325+
}
326+
264327
func TestAlertProvider_GetAlertStatePlaceholderValueDefaults(t *testing.T) {
265328
alertProvider := &AlertProvider{
266329
DefaultConfig: Config{

0 commit comments

Comments
 (0)