mirror of
https://github.com/grafana/grafana.git
synced 2025-07-30 00:42:27 +08:00

* Alerting: Contact point export snapshot test * Fix Jira.fields hcl export type to allow map[string]any Since gohcl does not support this type, we marshal as a string instead which will be handled in the TF provider. * mapToJSONStringCodec encoder needed for TestContactPointFromContactPointExports
314 lines
9.9 KiB
Go
314 lines
9.9 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/grafana/alerting/notify"
|
|
receiversTesting "github.com/grafana/alerting/receivers/testing"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
apicompat "github.com/grafana/grafana/pkg/services/ngalert/api/compat"
|
|
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
|
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
|
"github.com/grafana/grafana/pkg/services/ngalert/provisioning"
|
|
"github.com/grafana/grafana/pkg/util"
|
|
)
|
|
|
|
// Test that conversion notify.APIReceiver -> definitions.ContactPoint -> notify.APIReceiver does not lose data
|
|
func TestContactPointFromContactPointExports(t *testing.T) {
|
|
getContactPointExport := func(t *testing.T, receiver *notify.APIReceiver) definitions.ContactPointExport {
|
|
export := make([]definitions.ReceiverExport, 0, len(receiver.Integrations))
|
|
for _, integrationConfig := range receiver.Integrations {
|
|
postable := &definitions.PostableGrafanaReceiver{
|
|
UID: integrationConfig.UID,
|
|
Name: integrationConfig.Name,
|
|
Type: integrationConfig.Type,
|
|
DisableResolveMessage: integrationConfig.DisableResolveMessage,
|
|
Settings: definitions.RawMessage(integrationConfig.Settings),
|
|
SecureSettings: integrationConfig.SecureSettings,
|
|
}
|
|
emb, err := provisioning.PostableGrafanaReceiverToEmbeddedContactPoint(
|
|
postable,
|
|
models.ProvenanceNone,
|
|
func(s string) string { // test configs are not encrypted but encoded
|
|
d, err := base64.StdEncoding.DecodeString(s)
|
|
require.NoError(t, err)
|
|
return string(d)
|
|
})
|
|
require.NoError(t, err)
|
|
ex, err := apicompat.ReceiverExportFromEmbeddedContactPoint(emb)
|
|
require.NoError(t, err)
|
|
export = append(export, ex)
|
|
}
|
|
|
|
return definitions.ContactPointExport{
|
|
OrgID: 1,
|
|
Name: receiver.Name,
|
|
Receivers: export,
|
|
}
|
|
}
|
|
|
|
// use the configs for testing because they have all fields supported by integrations
|
|
for integrationType, cfg := range notify.AllKnownConfigsForTesting {
|
|
t.Run(integrationType, func(t *testing.T) {
|
|
recCfg := ¬ify.APIReceiver{
|
|
ConfigReceiver: notify.ConfigReceiver{Name: "test-receiver"},
|
|
GrafanaIntegrations: notify.GrafanaIntegrations{
|
|
Integrations: []*notify.GrafanaIntegrationConfig{
|
|
cfg.GetRawNotifierConfig("test"),
|
|
},
|
|
},
|
|
}
|
|
|
|
expected, err := notify.BuildReceiverConfiguration(context.Background(), recCfg, notify.DecodeSecretsFromBase64, func(ctx context.Context, sjd map[string][]byte, key string, fallback string) string {
|
|
return receiversTesting.DecryptForTesting(sjd)(key, fallback)
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
result, err := ContactPointFromContactPointExport(getContactPointExport(t, recCfg))
|
|
require.NoError(t, err)
|
|
|
|
back, err := ContactPointToContactPointExport(result)
|
|
require.NoError(t, err)
|
|
|
|
actual, err := notify.BuildReceiverConfiguration(context.Background(), &back, notify.DecodeSecretsFromBase64, func(ctx context.Context, sjd map[string][]byte, key string, fallback string) string {
|
|
return receiversTesting.DecryptForTesting(sjd)(key, fallback)
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
pathFilters := []string{
|
|
"Metadata.UID",
|
|
"Metadata.Name",
|
|
"WecomConfigs.Settings.EndpointURL", // This field is not exposed to user
|
|
}
|
|
if integrationType != "webhook" {
|
|
// Many notifiers now support HTTPClientConfig but only Webhook currently has it enabled in schema.
|
|
//TODO: Remove this once HTTPClientConfig is added to other schemas.
|
|
pathFilters = append(pathFilters, "HTTPClientConfig")
|
|
}
|
|
pathFilter := cmp.FilterPath(func(path cmp.Path) bool {
|
|
for _, filter := range pathFilters {
|
|
if strings.Contains(path.String(), filter) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}, cmp.Ignore())
|
|
|
|
diff := cmp.Diff(expected, actual, pathFilter)
|
|
if len(diff) != 0 {
|
|
require.Failf(t, "The re-marshalled configuration does not match the expected one", diff)
|
|
}
|
|
})
|
|
}
|
|
|
|
t.Run("pushover optional numbers as string", func(t *testing.T) {
|
|
export := definitions.ContactPointExport{
|
|
Name: "test",
|
|
Receivers: []definitions.ReceiverExport{
|
|
{
|
|
Type: "pushover",
|
|
Settings: definitions.RawMessage(
|
|
`{
|
|
"priority": 1,
|
|
"okPriority": "2",
|
|
"expire": null,
|
|
"retry": "invalid"
|
|
}`),
|
|
},
|
|
},
|
|
}
|
|
result, err := ContactPointFromContactPointExport(export)
|
|
require.NoError(t, err)
|
|
require.Len(t, result.Pushover, 1)
|
|
require.Equal(t, int64(1), *result.Pushover[0].AlertingPriority)
|
|
require.Equal(t, int64(2), *result.Pushover[0].OKPriority)
|
|
require.Nil(t, result.Pushover[0].Expire)
|
|
require.Nil(t, result.Pushover[0].Retry)
|
|
})
|
|
t.Run("email with multiple addresses", func(t *testing.T) {
|
|
export := definitions.ContactPointExport{
|
|
Name: "test",
|
|
Receivers: []definitions.ReceiverExport{
|
|
{
|
|
Type: "email",
|
|
Settings: definitions.RawMessage(`{"addresses": "test@grafana.com,test2@grafana.com;test3@grafana.com\ntest4@granafa.com"}`),
|
|
},
|
|
},
|
|
}
|
|
result, err := ContactPointFromContactPointExport(export)
|
|
require.NoError(t, err)
|
|
require.Len(t, result.Email, 1)
|
|
require.EqualValues(t, []string{
|
|
"test@grafana.com",
|
|
"test2@grafana.com",
|
|
"test3@grafana.com",
|
|
"test4@granafa.com",
|
|
}, result.Email[0].Addresses)
|
|
})
|
|
t.Run("webhook with optional numbers as string", func(t *testing.T) {
|
|
export := definitions.ContactPointExport{
|
|
Name: "test",
|
|
Receivers: []definitions.ReceiverExport{
|
|
{
|
|
Type: "webhook",
|
|
Settings: definitions.RawMessage(`{ "maxAlerts" : "112" }`),
|
|
},
|
|
{
|
|
Type: "webhook",
|
|
Settings: definitions.RawMessage(`{ "maxAlerts" : "test" }`),
|
|
},
|
|
{
|
|
Type: "webhook",
|
|
Settings: definitions.RawMessage(`{ "maxAlerts" : null }`),
|
|
},
|
|
},
|
|
}
|
|
result, err := ContactPointFromContactPointExport(export)
|
|
require.NoError(t, err)
|
|
require.Len(t, result.Webhook, 3)
|
|
require.Equal(t, int64(112), *result.Webhook[0].MaxAlerts)
|
|
require.Nil(t, result.Webhook[1].MaxAlerts)
|
|
require.Nil(t, result.Webhook[2].MaxAlerts)
|
|
})
|
|
|
|
t.Run("oncall with optional numbers as string", func(t *testing.T) {
|
|
export := definitions.ContactPointExport{
|
|
Name: "test",
|
|
Receivers: []definitions.ReceiverExport{
|
|
{
|
|
Type: "oncall",
|
|
Settings: definitions.RawMessage(`{ "maxAlerts" : "112" }`),
|
|
},
|
|
{
|
|
Type: "oncall",
|
|
Settings: definitions.RawMessage(`{ "maxAlerts" : "test" }`),
|
|
},
|
|
{
|
|
Type: "oncall",
|
|
Settings: definitions.RawMessage(`{ "maxAlerts" : null }`),
|
|
},
|
|
},
|
|
}
|
|
result, err := ContactPointFromContactPointExport(export)
|
|
require.NoError(t, err)
|
|
require.Len(t, result.OnCall, 3)
|
|
require.Equal(t, int64(112), *result.OnCall[0].MaxAlerts)
|
|
require.Nil(t, result.OnCall[1].MaxAlerts)
|
|
require.Nil(t, result.OnCall[2].MaxAlerts)
|
|
})
|
|
|
|
t.Run("mqtt with optional numbers as string", func(t *testing.T) {
|
|
export := definitions.ContactPointExport{
|
|
Name: "test",
|
|
Receivers: []definitions.ReceiverExport{
|
|
{
|
|
Type: "mqtt",
|
|
Settings: definitions.RawMessage(`{ "qos" : "112" }`),
|
|
},
|
|
{
|
|
Type: "mqtt",
|
|
Settings: definitions.RawMessage(`{ "qos" : "test" }`),
|
|
},
|
|
{
|
|
Type: "mqtt",
|
|
Settings: definitions.RawMessage(`{ "qos" : null }`),
|
|
},
|
|
},
|
|
}
|
|
result, err := ContactPointFromContactPointExport(export)
|
|
require.NoError(t, err)
|
|
require.Len(t, result.Mqtt, 3)
|
|
require.Equal(t, int64(112), *result.Mqtt[0].QoS)
|
|
require.Nil(t, result.Mqtt[1].QoS)
|
|
require.Nil(t, result.Mqtt[2].QoS)
|
|
})
|
|
|
|
t.Run("jira with various fields values as string", func(t *testing.T) {
|
|
testcases := []struct {
|
|
name string
|
|
input definitions.RawMessage
|
|
expected *string
|
|
expectedErr bool
|
|
}{
|
|
{
|
|
name: "standard map[string]string",
|
|
input: definitions.RawMessage(`{ "fields" : {"test-data" : "test-value"} }`),
|
|
expected: util.Pointer(`{"test-data":"test-value"}`),
|
|
},
|
|
{
|
|
name: "map[string]int",
|
|
input: definitions.RawMessage(`{ "fields" : {"test-data" : 42} }`),
|
|
expected: util.Pointer(`{"test-data":42}`),
|
|
},
|
|
{
|
|
name: "map[string]interface{} with null value",
|
|
input: definitions.RawMessage(`{ "fields" : {"test-data" : null} }`),
|
|
expected: util.Pointer(`{"test-data":null}`),
|
|
},
|
|
{
|
|
name: "null fields",
|
|
input: definitions.RawMessage(`{ "fields" : null }`),
|
|
expected: nil,
|
|
},
|
|
{
|
|
name: "empty map",
|
|
input: definitions.RawMessage(`{ "fields" : {} }`),
|
|
expected: util.Pointer(`{}`),
|
|
},
|
|
{
|
|
name: "nested map",
|
|
input: definitions.RawMessage(`{ "fields" : {"test-data" : {"test-data-nested" : "test-value-nested"}} }`),
|
|
expected: util.Pointer(`{"test-data":{"test-data-nested":"test-value-nested"}}`),
|
|
},
|
|
{
|
|
name: "nested slice",
|
|
input: definitions.RawMessage(`{ "fields" : {"test-data" : ["slice1", "slice2"]} }`),
|
|
expected: util.Pointer(`{"test-data":["slice1","slice2"]}`),
|
|
},
|
|
{
|
|
name: "string value",
|
|
input: definitions.RawMessage(`{ "fields" : "some string" }`),
|
|
expectedErr: true,
|
|
},
|
|
{
|
|
name: "slice",
|
|
input: definitions.RawMessage(`{ "fields" : ["slice1", "slice2"} }`),
|
|
expectedErr: true,
|
|
},
|
|
}
|
|
for _, tc := range testcases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
export := definitions.ContactPointExport{
|
|
Name: "test",
|
|
Receivers: []definitions.ReceiverExport{
|
|
{
|
|
Type: "jira",
|
|
Settings: tc.input,
|
|
},
|
|
},
|
|
}
|
|
|
|
result, err := ContactPointFromContactPointExport(export)
|
|
if tc.expectedErr {
|
|
require.Error(t, err, "Expected error for input: %s", tc.input)
|
|
return
|
|
} else {
|
|
require.NoError(t, err, "Unexpected error for input: %s", tc.input)
|
|
}
|
|
require.Len(t, result.Jira, 1)
|
|
|
|
if tc.expected == nil {
|
|
require.Nil(t, result.Jira[0].Fields)
|
|
} else {
|
|
require.Equal(t, *tc.expected, *result.Jira[0].Fields)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|