diff --git a/README.md b/README.md index 179923a4..0a1da3ec 100644 --- a/README.md +++ b/README.md @@ -2450,7 +2450,8 @@ Furthermore, you may use the following placeholders in the body (`alerting.custo - `[ENDPOINT_GROUP]` (resolved from `endpoints[].group`) - `[ENDPOINT_URL]` (resolved from `endpoints[].url`) - `[RESULT_ERRORS]` (resolved from the health evaluation of a given health check) - +- `[RESULT_CONDITIONS]` (condition results from the health evaluation of a given health check) +- If you have an alert using the `custom` provider with `send-on-resolved` set to `true`, you can use the `[ALERT_TRIGGERED_OR_RESOLVED]` placeholder to differentiate the notifications. The aforementioned placeholder will be replaced by `TRIGGERED` or `RESOLVED` accordingly, though it can be modified diff --git a/alerting/provider/custom/custom.go b/alerting/provider/custom/custom.go index 2e4da58b..724916e0 100644 --- a/alerting/provider/custom/custom.go +++ b/alerting/provider/custom/custom.go @@ -111,6 +111,25 @@ func (provider *AlertProvider) buildHTTPRequest(cfg *Config, ep *endpoint.Endpoi resultErrors := strings.ReplaceAll(strings.Join(result.Errors, ","), "\"", "\\\"") body = strings.ReplaceAll(body, "[RESULT_ERRORS]", resultErrors) url = strings.ReplaceAll(url, "[RESULT_ERRORS]", resultErrors) + + if len(result.ConditionResults) > 0 && strings.Contains(body, "[RESULT_CONDITIONS]") { + var formattedConditionResults string + for index, conditionResult := range result.ConditionResults { + var prefix string + if conditionResult.Success { + prefix = "✅" + } else { + prefix = "❌" + } + formattedConditionResults += fmt.Sprintf("%s - `%s`", prefix, conditionResult.Condition) + if index < len(result.ConditionResults)-1 { + formattedConditionResults += ", " + } + } + body = strings.ReplaceAll(body, "[RESULT_CONDITIONS]", formattedConditionResults) + url = strings.ReplaceAll(url, "[RESULT_CONDITIONS]", formattedConditionResults) + } + if resolved { body = strings.ReplaceAll(body, "[ALERT_TRIGGERED_OR_RESOLVED]", provider.GetAlertStatePlaceholderValue(cfg, true)) url = strings.ReplaceAll(url, "[ALERT_TRIGGERED_OR_RESOLVED]", provider.GetAlertStatePlaceholderValue(cfg, true)) diff --git a/alerting/provider/custom/custom_test.go b/alerting/provider/custom/custom_test.go index 7536f392..c3372cc0 100644 --- a/alerting/provider/custom/custom_test.go +++ b/alerting/provider/custom/custom_test.go @@ -261,6 +261,69 @@ func TestAlertProvider_buildHTTPRequestWithCustomPlaceholder(t *testing.T) { } } +func TestAlertProvider_buildHTTPRequestWithCustomPlaceholderAndResultConditions(t *testing.T) { + alertProvider := &AlertProvider{ + DefaultConfig: Config{ + URL: "https://example.com/[ENDPOINT_GROUP]/[ENDPOINT_NAME]?event=[ALERT_TRIGGERED_OR_RESOLVED]&description=[ALERT_DESCRIPTION]", + Body: "[ENDPOINT_NAME],[ENDPOINT_GROUP],[ALERT_DESCRIPTION],[ALERT_TRIGGERED_OR_RESOLVED],[RESULT_CONDITIONS]", + Headers: nil, + Placeholders: map[string]map[string]string{ + "ALERT_TRIGGERED_OR_RESOLVED": { + "RESOLVED": "fixed", + "TRIGGERED": "boom", + }, + }, + }, + } + alertDescription := "alert-description" + scenarios := []struct { + AlertProvider *AlertProvider + Resolved bool + ExpectedURL string + ExpectedBody string + NoConditions bool + }{ + { + AlertProvider: alertProvider, + Resolved: true, + ExpectedURL: "https://example.com/endpoint-group/endpoint-name?event=fixed&description=alert-description", + ExpectedBody: "endpoint-name,endpoint-group,alert-description,fixed,✅ - `[CONNECTED] == true`, ✅ - `[STATUS] == 200`", + }, + { + AlertProvider: alertProvider, + Resolved: false, + ExpectedURL: "https://example.com/endpoint-group/endpoint-name?event=boom&description=alert-description", + ExpectedBody: "endpoint-name,endpoint-group,alert-description,boom,❌ - `[CONNECTED] == true`, ❌ - `[STATUS] == 200`", + }, + } + for _, scenario := range scenarios { + t.Run(fmt.Sprintf("resolved-%v-with-custom-placeholders", scenario.Resolved), func(t *testing.T) { + var conditionResults []*endpoint.ConditionResult + if !scenario.NoConditions { + conditionResults = []*endpoint.ConditionResult{ + {Condition: "[CONNECTED] == true", Success: scenario.Resolved}, + {Condition: "[STATUS] == 200", Success: scenario.Resolved}, + } + } + + request := alertProvider.buildHTTPRequest( + &alertProvider.DefaultConfig, + &endpoint.Endpoint{Name: "endpoint-name", Group: "endpoint-group"}, + &alert.Alert{Description: &alertDescription}, + &endpoint.Result{ConditionResults: conditionResults}, + scenario.Resolved, + ) + if request.URL.String() != scenario.ExpectedURL { + t.Error("expected URL to be", scenario.ExpectedURL, "got", request.URL.String()) + } + body, _ := io.ReadAll(request.Body) + if string(body) != scenario.ExpectedBody { + t.Error("expected body to be", scenario.ExpectedBody, "got", string(body)) + } + }) + } +} + func TestAlertProvider_GetAlertStatePlaceholderValueDefaults(t *testing.T) { alertProvider := &AlertProvider{ DefaultConfig: Config{