From b8f23eacd4acdfd51655c2806cc250456e1f11af Mon Sep 17 00:00:00 2001 From: Yuri Tseretyan Date: Fri, 26 Sep 2025 09:31:50 -0400 Subject: [PATCH] Alerting: Migrate to integration schema (#111643) * update tests to assert against snapshot * remove channel_config package replaced by schemas from alerting module * update references to use new schema --- apps/iam/go.mod | 2 +- apps/iam/go.sum | 4 +- go.mod | 2 +- go.sum | 4 +- pkg/api/alerting.go | 39 +- .../ngalert/api/api_provisioning_test.go | 3 +- pkg/services/ngalert/models/receivers.go | 38 +- pkg/services/ngalert/models/receivers_test.go | 73 +- pkg/services/ngalert/models/testing.go | 23 +- .../channels_config/available_channels.go | 2154 -------- .../available_channels_mimir.go | 1572 ------ .../available_channels_test.go | 290 -- .../notifier/channels_config/plugin.go | 122 - pkg/services/ngalert/notifier/crypto.go | 12 +- pkg/services/ngalert/notifier/email_test.go | 2 +- .../notifier/legacy_storage/receivers_test.go | 18 +- .../ngalert/provisioning/contactpoints.go | 18 +- .../provisioning/contactpoints_test.go | 9 +- .../alerting/api_available_channel_test.go | 14 +- .../alerting/api_notification_channel_test.go | 10 +- .../alert-notifiers-v1-snapshot.json | 4310 +++++++++++++++++ .../alert-notifiers-v2-snapshot.json | 3 +- .../notifications/receivers/receiver_test.go | 8 +- 23 files changed, 4475 insertions(+), 4255 deletions(-) delete mode 100644 pkg/services/ngalert/notifier/channels_config/available_channels.go delete mode 100644 pkg/services/ngalert/notifier/channels_config/available_channels_mimir.go delete mode 100644 pkg/services/ngalert/notifier/channels_config/available_channels_test.go delete mode 100644 pkg/services/ngalert/notifier/channels_config/plugin.go create mode 100644 pkg/tests/api/alerting/test-data/alert-notifiers-v1-snapshot.json diff --git a/apps/iam/go.mod b/apps/iam/go.mod index 64c5a52c1a8..a7ac326bd6c 100644 --- a/apps/iam/go.mod +++ b/apps/iam/go.mod @@ -204,7 +204,7 @@ require ( github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect github.com/googleapis/gax-go/v2 v2.14.2 // indirect github.com/gorilla/mux v1.8.1 // indirect - github.com/grafana/alerting v0.0.0-20250925193206-bd061d3d9185 // indirect + github.com/grafana/alerting v0.0.0-20250925200825-7a889aa4934d // indirect github.com/grafana/authlib/types v0.0.0-20250917093142-83a502239781 // indirect github.com/grafana/dataplane/sdata v0.0.9 // indirect github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 // indirect diff --git a/apps/iam/go.sum b/apps/iam/go.sum index b93048ed57a..b561836b7fd 100644 --- a/apps/iam/go.sum +++ b/apps/iam/go.sum @@ -721,8 +721,8 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= -github.com/grafana/alerting v0.0.0-20250925193206-bd061d3d9185 h1:R494uXJOz7glN76hJXKjbwu+VBYFsT0CFprsXmdHla0= -github.com/grafana/alerting v0.0.0-20250925193206-bd061d3d9185/go.mod h1:T5sitas9VhVj8/S9LeRLy6H75kTBdh/sCCqHo7gaQI8= +github.com/grafana/alerting v0.0.0-20250925200825-7a889aa4934d h1:zzEty7HgfXbQ/RiBCJFMqaZiJlqiXuz/Zbc6/H6ksuM= +github.com/grafana/alerting v0.0.0-20250925200825-7a889aa4934d/go.mod h1:T5sitas9VhVj8/S9LeRLy6H75kTBdh/sCCqHo7gaQI8= github.com/grafana/authlib v0.0.0-20250924100039-ea07223cdb6c h1:8GIMe1KclDdfogaeRsiU69Ev2zTF9kmjqjQqqZMzerc= github.com/grafana/authlib v0.0.0-20250924100039-ea07223cdb6c/go.mod h1:C6CmTG6vfiqebjJswKsc6zes+1F/OtTCi6aAtL5Um6A= github.com/grafana/authlib/types v0.0.0-20250917093142-83a502239781 h1:jymmOFIWnW26DeUjFgYEoltI170KeT5r1rI8a/dUf0E= diff --git a/go.mod b/go.mod index 5a986999c43..5ea667a79cf 100644 --- a/go.mod +++ b/go.mod @@ -86,7 +86,7 @@ require ( github.com/googleapis/gax-go/v2 v2.14.2 // @grafana/grafana-backend-group github.com/gorilla/mux v1.8.1 // @grafana/grafana-backend-group github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // @grafana/grafana-app-platform-squad - github.com/grafana/alerting v0.0.0-20250925193206-bd061d3d9185 // @grafana/alerting-backend + github.com/grafana/alerting v0.0.0-20250925200825-7a889aa4934d // @grafana/alerting-backend github.com/grafana/authlib v0.0.0-20250924100039-ea07223cdb6c // @grafana/identity-access-team github.com/grafana/authlib/types v0.0.0-20250917093142-83a502239781 // @grafana/identity-access-team github.com/grafana/dataplane/examples v0.0.1 // @grafana/observability-metrics diff --git a/go.sum b/go.sum index b444408d771..3a861217971 100644 --- a/go.sum +++ b/go.sum @@ -1585,8 +1585,8 @@ github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7Fsg github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= -github.com/grafana/alerting v0.0.0-20250925193206-bd061d3d9185 h1:R494uXJOz7glN76hJXKjbwu+VBYFsT0CFprsXmdHla0= -github.com/grafana/alerting v0.0.0-20250925193206-bd061d3d9185/go.mod h1:T5sitas9VhVj8/S9LeRLy6H75kTBdh/sCCqHo7gaQI8= +github.com/grafana/alerting v0.0.0-20250925200825-7a889aa4934d h1:zzEty7HgfXbQ/RiBCJFMqaZiJlqiXuz/Zbc6/H6ksuM= +github.com/grafana/alerting v0.0.0-20250925200825-7a889aa4934d/go.mod h1:T5sitas9VhVj8/S9LeRLy6H75kTBdh/sCCqHo7gaQI8= github.com/grafana/authlib v0.0.0-20250924100039-ea07223cdb6c h1:8GIMe1KclDdfogaeRsiU69Ev2zTF9kmjqjQqqZMzerc= github.com/grafana/authlib v0.0.0-20250924100039-ea07223cdb6c/go.mod h1:C6CmTG6vfiqebjJswKsc6zes+1F/OtTCi6aAtL5Um6A= github.com/grafana/authlib/types v0.0.0-20250917093142-83a502239781 h1:jymmOFIWnW26DeUjFgYEoltI170KeT5r1rI8a/dUf0E= diff --git a/pkg/api/alerting.go b/pkg/api/alerting.go index 2656ea3e1f0..27abb6ebbfc 100644 --- a/pkg/api/alerting.go +++ b/pkg/api/alerting.go @@ -5,19 +5,48 @@ import ( "slices" "strings" + "github.com/grafana/alerting/notify" + "github.com/grafana/alerting/receivers/schema" + "github.com/grafana/grafana/pkg/api/response" contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" - "github.com/grafana/grafana/pkg/services/ngalert/notifier/channels_config" ) func (hs *HTTPServer) GetAlertNotifiers() func(*contextmodel.ReqContext) response.Response { return func(r *contextmodel.ReqContext) response.Response { + v2 := notify.GetSchemaForAllIntegrations() + slices.SortFunc(v2, func(a, b schema.IntegrationTypeSchema) int { + return strings.Compare(string(a.Type), string(b.Type)) + }) if r.Query("version") == "2" { - v2 := slices.SortedFunc(channels_config.GetAvailableNotifiersV2(), func(a, b *channels_config.VersionedNotifierPlugin) int { - return strings.Compare(a.Type, b.Type) - }) return response.JSON(http.StatusOK, v2) } - return response.JSON(http.StatusOK, channels_config.GetAvailableNotifiers()) + + type NotifierPlugin struct { + Type string `json:"type"` + TypeAlias string `json:"typeAlias,omitempty"` + Name string `json:"name"` + Heading string `json:"heading"` + Description string `json:"description"` + Info string `json:"info"` + Options []schema.Field `json:"options"` + } + + result := make([]*NotifierPlugin, 0, len(v2)) + for _, s := range v2 { + v1, ok := s.GetVersion(schema.V1) + if !ok { + continue + } + result = append(result, &NotifierPlugin{ + Type: string(s.Type), + Name: s.Name, + Description: s.Description, + Heading: s.Heading, + Info: s.Info, + Options: v1.Options, + }) + } + return response.JSON(http.StatusOK, result) } } diff --git a/pkg/services/ngalert/api/api_provisioning_test.go b/pkg/services/ngalert/api/api_provisioning_test.go index 81ad6fac9d9..a7dd8f1f2fe 100644 --- a/pkg/services/ngalert/api/api_provisioning_test.go +++ b/pkg/services/ngalert/api/api_provisioning_test.go @@ -15,6 +15,7 @@ import ( "time" alertingNotify "github.com/grafana/alerting/notify" + "github.com/grafana/alerting/receivers/schema" prometheus "github.com/prometheus/alertmanager/config" "github.com/prometheus/alertmanager/pkg/labels" "github.com/prometheus/alertmanager/timeinterval" @@ -2037,7 +2038,7 @@ func TestApiContactPointExportSnapshot(t *testing.T) { integration := models.IntegrationGen( models.IntegrationMuts.WithName(allIntegrationsName), models.IntegrationMuts.WithUID(fmt.Sprintf("%s-uid", integrationType)), - models.IntegrationMuts.WithValidConfig(integrationType), + models.IntegrationMuts.WithValidConfig(schema.IntegrationType(integrationType)), )() integration.DisableResolveMessage = redacted allIntegrations = append(allIntegrations, integration) diff --git a/pkg/services/ngalert/models/receivers.go b/pkg/services/ngalert/models/receivers.go index 8d942975b5e..1f892147938 100644 --- a/pkg/services/ngalert/models/receivers.go +++ b/pkg/services/ngalert/models/receivers.go @@ -13,8 +13,7 @@ import ( "strings" alertingNotify "github.com/grafana/alerting/notify" - - "github.com/grafana/grafana/pkg/services/ngalert/notifier/channels_config" + "github.com/grafana/alerting/receivers/schema" ) // GetReceiverQuery represents a query for a single receiver. @@ -228,30 +227,35 @@ func (f IntegrationFieldPath) With(segment string) IntegrationFieldPath { // IntegrationConfig - The integration configuration // error - Error if integration type not found or invalid version specified func IntegrationConfigFromType(integrationType string, version *string) (IntegrationConfig, error) { - versionConfig, err := channels_config.ConfigForIntegrationType(integrationType) - if err != nil { - return IntegrationConfig{}, err + typeSchema, ok := alertingNotify.GetSchemaForIntegration(schema.IntegrationType(integrationType)) + if !ok { + return IntegrationConfig{}, fmt.Errorf("integration type %s not found", integrationType) } - // if particular version is requested and the version returned does not match, try to get the correct version - if version != nil && *version != string(versionConfig.Version) { - exists := false - versionConfig, exists = versionConfig.Plugin.GetVersion(channels_config.NotifierVersion(*version)) - if !exists { - return IntegrationConfig{}, fmt.Errorf("version %s not found in config", *version) - } + if version == nil { + return IntegrationConfigFromSchema(typeSchema, typeSchema.CurrentVersion) + } + return IntegrationConfigFromSchema(typeSchema, schema.Version(*version)) +} + +// IntegrationConfigFromSchema returns an integration configuration for a given version of the integration type schema. +// Returns an error if the schema does not have such version +func IntegrationConfigFromSchema(typeSchema schema.IntegrationTypeSchema, version schema.Version) (IntegrationConfig, error) { + typeVersion, ok := typeSchema.GetVersion(version) + if !ok { + return IntegrationConfig{}, fmt.Errorf("version %s not found in config", version) } integrationConfig := IntegrationConfig{ - Type: versionConfig.Plugin.Type, - Version: string(versionConfig.Version), - Fields: make(map[string]IntegrationField, len(versionConfig.Options)), + Type: string(typeSchema.Type), + Version: string(typeVersion.Version), + Fields: make(map[string]IntegrationField, len(typeVersion.Options)), } - for _, option := range versionConfig.Options { + for _, option := range typeVersion.Options { integrationConfig.Fields[option.PropertyName] = notifierOptionToIntegrationField(option) } return integrationConfig, nil } -func notifierOptionToIntegrationField(option channels_config.NotifierOption) IntegrationField { +func notifierOptionToIntegrationField(option schema.Field) IntegrationField { f := IntegrationField{ Name: option.PropertyName, Secure: option.Secure, diff --git a/pkg/services/ngalert/models/receivers_test.go b/pkg/services/ngalert/models/receivers_test.go index 3b691953edd..0c6c08c3d93 100644 --- a/pkg/services/ngalert/models/receivers_test.go +++ b/pkg/services/ngalert/models/receivers_test.go @@ -6,11 +6,10 @@ import ( "testing" alertingNotify "github.com/grafana/alerting/notify" + "github.com/grafana/alerting/receivers/schema" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/grafana/grafana/pkg/services/ngalert/notifier/channels_config" - "github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util/testutil" ) @@ -41,14 +40,14 @@ func TestReceiver_EncryptDecrypt(t *testing.T) { encryptFn := Base64Enrypt decryptnFn := Base64Decrypt // Test that all known integration types encrypt and decrypt their secrets. - for integrationType := range alertingNotify.AllKnownConfigsForTesting { - t.Run(integrationType, func(t *testing.T) { + for it := range alertingNotify.AllKnownConfigsForTesting { + integrationType := schema.IntegrationType(it) + t.Run(string(integrationType), func(t *testing.T) { decrypedIntegration := IntegrationGen(IntegrationMuts.WithValidConfig(integrationType))() - encrypted := decrypedIntegration.Clone() - secrets, err := channels_config.GetSecretKeysForContactPointType(integrationType, channels_config.V1) - assert.NoError(t, err) - for _, key := range secrets { + typeVersion, ok := alertingNotify.GetSchemaVersionForIntegration(integrationType, schema.V1) + require.True(t, ok) + for _, key := range typeVersion.GetSecretFieldsPaths() { val, ok, err := extractField(encrypted.Settings, NewIntegrationFieldPath(key)) assert.NoError(t, err) if ok { @@ -59,7 +58,7 @@ func TestReceiver_EncryptDecrypt(t *testing.T) { } testIntegration := decrypedIntegration.Clone() - err = testIntegration.Encrypt(encryptFn) + err := testIntegration.Encrypt(encryptFn) assert.NoError(t, err) require.Equal(t, encrypted, testIntegration) @@ -77,14 +76,15 @@ func TestIntegration_Redact(t *testing.T) { return "TESTREDACTED" } // Test that all known integration types redact their secrets. - for integrationType := range alertingNotify.AllKnownConfigsForTesting { - t.Run(integrationType, func(t *testing.T) { + for it := range alertingNotify.AllKnownConfigsForTesting { + integrationType := schema.IntegrationType(it) + t.Run(string(integrationType), func(t *testing.T) { validIntegration := IntegrationGen(IntegrationMuts.WithValidConfig(integrationType))() expected := validIntegration.Clone() - secrets, err := channels_config.GetSecretKeysForContactPointType(integrationType, channels_config.V1) - assert.NoError(t, err) - for _, key := range secrets { + version, ok := alertingNotify.GetSchemaVersionForIntegration(integrationType, schema.V1) + require.True(t, ok) + for _, key := range version.GetSecretFieldsPaths() { err := setField(expected.Settings, NewIntegrationFieldPath(key), func(current any) any { if s, isString := current.(string); isString && s != "" { delete(expected.SecureSettings, key) @@ -106,8 +106,9 @@ func TestIntegration_Validate(t *testing.T) { testutil.SkipIntegrationTestInShortMode(t) // Test that all known integration types are valid. - for integrationType := range alertingNotify.AllKnownConfigsForTesting { - t.Run(integrationType, func(t *testing.T) { + for it := range alertingNotify.AllKnownConfigsForTesting { + integrationType := schema.IntegrationType(it) + t.Run(string(integrationType), func(t *testing.T) { validIntegration := IntegrationGen(IntegrationMuts.WithValidConfig(integrationType))() assert.NoError(t, validIntegration.Encrypt(Base64Enrypt)) assert.NoErrorf(t, validIntegration.Validate(Base64Decrypt), "integration should be valid") @@ -241,19 +242,19 @@ func TestIntegration_WithExistingSecureFields(t *testing.T) { func TestSecretsIntegrationConfig(t *testing.T) { // Test that all known integration types have a config and correctly mark their secrets as secure. - for integrationType := range alertingNotify.AllKnownConfigsForTesting { - t.Run(integrationType, func(t *testing.T) { - config, err := IntegrationConfigFromType(integrationType, nil) + for it := range alertingNotify.AllKnownConfigsForTesting { + integrationType := schema.IntegrationType(it) + t.Run(string(integrationType), func(t *testing.T) { + schemaType, ok := alertingNotify.GetSchemaForIntegration(integrationType) + require.True(t, ok) + + config, err := IntegrationConfigFromSchema(schemaType, schema.V1) assert.NoError(t, err) - t.Run("v1 is current", func(t *testing.T) { - configv1, err := IntegrationConfigFromType(integrationType, util.Pointer(string(channels_config.V1))) - assert.NoError(t, err) - assert.Equal(t, config, configv1) - }) + version, ok := schemaType.GetVersion(schema.V1) + require.True(t, ok) - secrets, err := channels_config.GetSecretKeysForContactPointType(integrationType, channels_config.V1) - assert.NoError(t, err) + secrets := version.GetSecretFieldsPaths() allSecrets := make(map[string]struct{}, len(secrets)) for _, key := range secrets { allSecrets[key] = struct{}{} @@ -270,17 +271,12 @@ func TestSecretsIntegrationConfig(t *testing.T) { }) } - t.Run("Unknown type returns error", func(t *testing.T) { - _, err := IntegrationConfigFromType("__--**unknown_type**--__", nil) - assert.Error(t, err) - }) - t.Run("Unknown version returns error", func(t *testing.T) { - version := util.Pointer("__--**unknown_version**--__") - types := maps.Keys(alertingNotify.AllKnownConfigsForTesting) - for itype := range types { - _, err := IntegrationConfigFromType(itype, version) - assert.Errorf(t, err, "unknown version for integration type %s did not return error but should", itype) + for s := range maps.Keys(alertingNotify.AllKnownConfigsForTesting) { + schemaType, _ := alertingNotify.GetSchemaForIntegration(schema.IntegrationType(s)) + _, err := IntegrationConfigFromSchema(schemaType, "unknown") + require.Error(t, err) + return } }) } @@ -289,8 +285,9 @@ func TestIntegration_SecureFields(t *testing.T) { testutil.SkipIntegrationTestInShortMode(t) // Test that all known integration types have a config and correctly mark their secrets as secure. - for integrationType := range alertingNotify.AllKnownConfigsForTesting { - t.Run(integrationType, func(t *testing.T) { + for it := range alertingNotify.AllKnownConfigsForTesting { + integrationType := schema.IntegrationType(it) + t.Run(string(integrationType), func(t *testing.T) { t.Run("contains SecureSettings", func(t *testing.T) { validIntegration := IntegrationGen(IntegrationMuts.WithValidConfig(integrationType))() expected := make(map[string]bool, len(validIntegration.SecureSettings)) diff --git a/pkg/services/ngalert/models/testing.go b/pkg/services/ngalert/models/testing.go index 7f124644f34..2164ca8f86f 100644 --- a/pkg/services/ngalert/models/testing.go +++ b/pkg/services/ngalert/models/testing.go @@ -13,6 +13,8 @@ import ( "github.com/go-openapi/strfmt" "github.com/google/uuid" alertingNotify "github.com/grafana/alerting/notify" + "github.com/grafana/alerting/receivers/schema" + "github.com/grafana/alerting/receivers/webex" "github.com/grafana/grafana-plugin-sdk-go/data" amv2 "github.com/prometheus/alertmanager/api/v2/models" "github.com/prometheus/alertmanager/pkg/labels" @@ -1209,7 +1211,7 @@ func (n ReceiverMutators) WithProvenance(provenance Provenance) Mutator[Receiver } } -func (n ReceiverMutators) WithValidIntegration(integrationType string) Mutator[Receiver] { +func (n ReceiverMutators) WithValidIntegration(integrationType schema.IntegrationType) Mutator[Receiver] { return func(r *Receiver) { // TODO add support for v0 integration := IntegrationGen(IntegrationMuts.WithValidConfig(integrationType))() @@ -1217,7 +1219,7 @@ func (n ReceiverMutators) WithValidIntegration(integrationType string) Mutator[R } } -func (n ReceiverMutators) WithInvalidIntegration(integrationType string) Mutator[Receiver] { +func (n ReceiverMutators) WithInvalidIntegration(integrationType schema.IntegrationType) Mutator[Receiver] { return func(r *Receiver) { // TODO add support for v0 integration := IntegrationGen(IntegrationMuts.WithInvalidConfig(integrationType))() @@ -1278,7 +1280,7 @@ func IntegrationGen(mutators ...Mutator[Integration]) func() Integration { SecureSettings: make(map[string]string), } - IntegrationMuts.WithValidConfig(randomIntegrationType)(&c) + IntegrationMuts.WithValidConfig(schema.IntegrationType(randomIntegrationType))(&c) for _, mutator := range mutators { mutator(&c) @@ -1312,11 +1314,12 @@ func (n IntegrationMutators) WithName(name string) Mutator[Integration] { } } -func (n IntegrationMutators) WithValidConfig(integrationType string) Mutator[Integration] { +func (n IntegrationMutators) WithValidConfig(integrationType schema.IntegrationType) Mutator[Integration] { return func(c *Integration) { // TODO add support for v0 integrations - config := alertingNotify.AllKnownConfigsForTesting[integrationType].GetRawNotifierConfig(c.Name) - integrationConfig, _ := IntegrationConfigFromType(integrationType, nil) + config := alertingNotify.AllKnownConfigsForTesting[string(integrationType)].GetRawNotifierConfig(c.Name) + typeSchema, _ := alertingNotify.GetSchemaForIntegration(integrationType) + integrationConfig, _ := IntegrationConfigFromSchema(typeSchema, schema.V1) c.Config = integrationConfig var settings map[string]any @@ -1332,13 +1335,13 @@ func (n IntegrationMutators) WithValidConfig(integrationType string) Mutator[Int } } -func (n IntegrationMutators) WithInvalidConfig(integrationType string) Mutator[Integration] { +func (n IntegrationMutators) WithInvalidConfig(integrationType schema.IntegrationType) Mutator[Integration] { return func(c *Integration) { - integrationConfig, _ := IntegrationConfigFromType(integrationType, nil) - c.Config = integrationConfig + typeSchema, _ := alertingNotify.GetSchemaForIntegration(integrationType) + c.Config, _ = IntegrationConfigFromSchema(typeSchema, schema.V1) c.Settings = map[string]interface{}{} c.SecureSettings = map[string]string{} - if integrationType == "webex" { + if integrationType == webex.Type { // Webex passes validation without any settings but should fail with an unparsable URL. c.Settings["api_url"] = "(*^$*^%!@#$*()" } diff --git a/pkg/services/ngalert/notifier/channels_config/available_channels.go b/pkg/services/ngalert/notifier/channels_config/available_channels.go deleted file mode 100644 index d16efb0348a..00000000000 --- a/pkg/services/ngalert/notifier/channels_config/available_channels.go +++ /dev/null @@ -1,2154 +0,0 @@ -package channels_config - -import ( - "fmt" - "iter" - "maps" - "strings" - - "github.com/grafana/alerting/receivers/jira" - alertingMqtt "github.com/grafana/alerting/receivers/mqtt" - alertingOpsgenie "github.com/grafana/alerting/receivers/opsgenie" - alertingPagerduty "github.com/grafana/alerting/receivers/pagerduty" - alertingTemplates "github.com/grafana/alerting/templates" -) - -type NotifierVersion string - -const ( - // versions that contain the "mimir" tag in their name are dedicated to integrations supported by Mimir. - // By default, all mimir integrations should use the V0mimir1 version. - // Exceptions are Mimir integrations that have multiple configurations for the same Grafana type. - - V0mimir1 NotifierVersion = "v0mimir1" - V0mimir2 NotifierVersion = "v0mimir2" - V1 NotifierVersion = "v1" -) - -// GetAvailableNotifiers returns the metadata of all the notification channels that can be configured. -func GetAvailableNotifiers() []*NotifierPlugin { - pushoverSoundOptions := []SelectOption{ - { - Value: "default", - Label: "Default", - }, - { - Value: "pushover", - Label: "Pushover", - }, { - Value: "bike", - Label: "Bike", - }, { - Value: "bugle", - Label: "Bugle", - }, { - Value: "cashregister", - Label: "Cashregister", - }, { - Value: "classical", - Label: "Classical", - }, { - Value: "cosmic", - Label: "Cosmic", - }, { - Value: "falling", - Label: "Falling", - }, { - Value: "gamelan", - Label: "Gamelan", - }, { - Value: "incoming", - Label: "Incoming", - }, { - Value: "intermission", - Label: "Intermission", - }, { - Value: "magic", - Label: "Magic", - }, { - Value: "mechanical", - Label: "Mechanical", - }, { - Value: "pianobar", - Label: "Pianobar", - }, { - Value: "siren", - Label: "Siren", - }, { - Value: "spacealarm", - Label: "Spacealarm", - }, { - Value: "tugboat", - Label: "Tugboat", - }, { - Value: "alien", - Label: "Alien", - }, { - Value: "climb", - Label: "Climb", - }, { - Value: "persistent", - Label: "Persistent", - }, { - Value: "echo", - Label: "Echo", - }, { - Value: "updown", - Label: "Updown", - }, { - Value: "none", - Label: "None", - }, - } - - pushoverPriorityOptions := []SelectOption{ - { - Value: "2", - Label: "Emergency", - }, - { - Value: "1", - Label: "High", - }, - { - Value: "0", - Label: "Normal", - }, - { - Value: "-1", - Label: "Low", - }, - { - Value: "-2", - Label: "Lowest", - }, - } - - tlsSubformOptions := func() []NotifierOption { - return []NotifierOption{ - { - Label: "Disable certificate verification", - Element: ElementTypeCheckbox, - Description: "Do not verify the server's certificate chain and host name.", - PropertyName: "insecureSkipVerify", - Required: false, - }, - { - Label: "CA Certificate", - Element: ElementTypeTextArea, - Description: "Certificate in PEM format to use when verifying the server's certificate chain.", - InputType: InputTypeText, - PropertyName: "caCertificate", - Required: false, - Secure: true, - }, - { - Label: "Client Certificate", - Element: ElementTypeTextArea, - Description: "Client certificate in PEM format to use when connecting to the server.", - InputType: InputTypeText, - PropertyName: "clientCertificate", - Required: false, - Secure: true, - }, - { - Label: "Client Key", - Element: ElementTypeTextArea, - Description: "Client key in PEM format to use when connecting to the server.", - InputType: InputTypeText, - PropertyName: "clientKey", - Required: false, - Secure: true, - }, - } - } - - proxyOption := func() NotifierOption { - return NotifierOption{ // New in 12.1. - Label: "Proxy Config", - PropertyName: "proxy_config", - Description: "Optional proxy configuration.", - Element: ElementTypeSubform, - SubformOptions: []NotifierOption{ - { - Label: "Proxy URL", - PropertyName: "proxy_url", - Description: "HTTP proxy server to use to connect to the targets.", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "https://proxy.example.com", - Required: false, - Secure: false, - }, - { - Label: "Proxy from environment", - PropertyName: "proxy_from_environment", - Description: "Use environment HTTP_PROXY, HTTPS_PROXY and NO_PROXY to determine proxies.", - Element: ElementTypeCheckbox, - Required: false, - Secure: false, - }, - { - Label: "No Proxy", - PropertyName: "no_proxy", - Description: "Comma-separated list of addresses that should not use a proxy.", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "example.com,1.2.3.4", - Required: false, - Secure: false, - }, - { - Label: "Proxy Connect Header", - PropertyName: "proxy_connect_header", - Description: "Optional headers to send to proxies during CONNECT requests.", - Element: ElementTypeKeyValueMap, - InputType: InputTypeText, - Required: false, - Secure: false, - }, - }, - } - } - - commonHttpClientOption := func() NotifierOption { - return NotifierOption{ // New in 12.1. - Label: "HTTP Config", - PropertyName: "http_config", - Description: "Common HTTP client options.", - Element: ElementTypeSubform, - SubformOptions: []NotifierOption{ - { // New in 12.1. - Label: "OAuth2", - PropertyName: "oauth2", - Description: "OAuth2 configuration options", - Element: ElementTypeSubform, - SubformOptions: []NotifierOption{ - { - Label: "Token URL", - PropertyName: "token_url", - Element: ElementTypeInput, - Description: "URL for the access token endpoint.", - InputType: InputTypeText, - Required: true, - Secure: false, - }, - { - Label: "Client ID", - PropertyName: "client_id", - Element: ElementTypeInput, - Description: "Client ID to use when authenticating.", - InputType: InputTypeText, - Required: true, - Secure: false, - }, - { - Label: "Client Secret", - PropertyName: "client_secret", - Element: ElementTypeInput, - Description: "Client secret to use when authenticating.", - InputType: InputTypeText, - Required: true, - Secure: true, - }, - { - Label: "Scopes", - PropertyName: "scopes", - Element: ElementStringArray, - Description: "Optional scopes to request when obtaining an access token.", - Required: false, - Secure: false, - }, - { - Label: "Endpoint Parameters", - PropertyName: "endpoint_params", - Element: ElementTypeKeyValueMap, - Description: "Optional parameters to append to the access token request.", - Required: false, - Secure: false, - }, - { - Label: "TLS", - PropertyName: "tls_config", - Description: "Optional TLS configuration options for OAuth2 requests.", - Element: ElementTypeSubform, - SubformOptions: tlsSubformOptions(), - }, - proxyOption(), - }, - }, - }, - } - } - - return []*NotifierPlugin{ - { - Type: "dingding", - Name: "DingDing", - Description: "Sends HTTP POST request to DingDing", - Heading: "DingDing settings", - Options: []NotifierOption{ - { - Label: "URL", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxx", - PropertyName: "url", - Required: true, - Secure: true, - }, - { - Label: "Message Type", - Element: ElementTypeSelect, - PropertyName: "msgType", - SelectOptions: []SelectOption{ - { - Value: "link", - Label: "Link"}, - { - Value: "actionCard", - Label: "ActionCard", - }, - }, - }, - { // New in 9.3. - Label: "Title", - Element: ElementTypeInput, - InputType: InputTypeText, - Description: "Templated title of the message", - Placeholder: alertingTemplates.DefaultMessageTitleEmbed, - PropertyName: "title", - }, - { // New in 8.0. - Label: "Message", - Element: ElementTypeTextArea, - Description: "Custom DingDing message. You can use template variables.", - Placeholder: alertingTemplates.DefaultMessageEmbed, - PropertyName: "message", - }, - }, - }, - { - Type: "kafka", - Name: "Kafka REST Proxy", - Description: "Sends notifications to Kafka Rest Proxy", - Heading: "Kafka settings", - Options: []NotifierOption{ - { - Label: "Kafka REST Proxy", - Element: ElementTypeInput, - InputType: InputTypeText, - Description: "Hint: If you are directly using v3 APIs hosted on a Confluent Kafka Server, you must append /kafka to the URL here. Example: https://localhost:8082/kafka", - Placeholder: "http://localhost:8082", - PropertyName: "kafkaRestProxy", - Required: true, - }, - { - Label: "Topic", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "topic1", - PropertyName: "kafkaTopic", - Required: true, - }, - { - Label: "Username", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "username", - Required: false, - }, - { - Label: "Password", - Element: ElementTypeInput, - InputType: InputTypePassword, - Description: "The password to use when making a call to the Kafka REST Proxy", - PropertyName: "password", - Required: false, - Secure: true, - }, - { - Label: "API version", - Element: ElementTypeSelect, - InputType: InputTypeText, - Description: "The API version to use when contacting the Kafka REST Server. By default v2 will be used.", - PropertyName: "apiVersion", - Required: false, - SelectOptions: []SelectOption{ - { - Value: "v2", - Label: "v2", - }, - { - Value: "v3", - Label: "v3", - }, - }, - }, - { - Label: "Cluster ID", - Element: ElementTypeInput, - InputType: InputTypeText, - Description: "v3 APIs require a clusterID to be specified.", - Placeholder: "lkc-abcde", - PropertyName: "kafkaClusterId", - Required: true, - ShowWhen: ShowWhen{ - Field: "apiVersion", - Is: "v3", - }, - }, - { - Label: "Description", - Element: ElementTypeTextArea, - InputType: InputTypeText, - Description: "Templated description of the Kafka message", - PropertyName: "description", - Placeholder: alertingTemplates.DefaultMessageTitleEmbed, - }, - { - Label: "Details", - Element: ElementTypeTextArea, - Description: "Custom details to include with the message. You can use template variables.", - PropertyName: "details", - Placeholder: alertingTemplates.DefaultMessageEmbed, - }, - }, - }, - { - Type: "email", - Name: "Email", - Description: "Sends notifications using Grafana server configured SMTP settings", - Heading: "Email settings", - Options: []NotifierOption{ - { - Label: "Single email", - Description: "Send a single email to all recipients", - Element: ElementTypeCheckbox, - PropertyName: "singleEmail", - }, - { - Label: "Addresses", - Description: "You can enter multiple email addresses using a \";\", \"\\n\" or \",\" separator", - Element: ElementTypeTextArea, - PropertyName: "addresses", - Required: true, - }, - { // New in 8.0. - Label: "Message", - Description: "Optional message. You can use templates to customize this field. Using a custom message will replace the default message", - Element: ElementTypeTextArea, - PropertyName: "message", - Placeholder: alertingTemplates.DefaultMessageEmbed, - }, - { // New in 9.0. - Label: "Subject", - Element: ElementTypeTextArea, - InputType: InputTypeText, - Description: "Optional subject. You can use templates to customize this field", - PropertyName: "subject", - Placeholder: alertingTemplates.DefaultMessageTitleEmbed, - }, - }, - }, - { - Type: "pagerduty", - Name: "PagerDuty", - Description: "Sends notifications to PagerDuty", - Heading: "PagerDuty settings", - Options: []NotifierOption{ - { - Label: "Integration Key", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "Pagerduty Integration Key", - PropertyName: "integrationKey", - Required: true, - Secure: true, - }, - { - Label: "Severity", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "critical", - Description: "Severity of the event. It must be critical, error, warning, info - otherwise, the default is set which is critical. You can use templates", - PropertyName: "severity", - }, - { // New in 8.0. - Label: "Class", - Description: "The class/type of the event, for example 'ping failure' or 'cpu load'", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "class", - }, - { // New in 8.0. - Label: "Component", - Description: "Component of the source machine that is responsible for the event, for example mysql or eth0", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "Grafana", - PropertyName: "component", - }, - { // New in 8.0. - Label: "Group", - Description: "Logical grouping of components of a service, for example 'app-stack'", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "group", - }, - { // New in 8.0. - Label: "Summary", - Description: "You can use templates for summary", - Element: ElementTypeTextArea, - InputType: InputTypeText, - Placeholder: alertingTemplates.DefaultMessageTitleEmbed, - PropertyName: "summary", - }, - { // New in 9.4. - Label: "Source", - Description: "The unique location of the affected system, preferably a hostname or FQDN. You can use templates", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "grafana.local", - PropertyName: "source", - }, - { // New in 9.4. - Label: "Client", - Description: "The name of the monitoring client that is triggering this event. You can use templates", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "Grafana", - PropertyName: "client", - }, - { // New in 9.4. - Label: "Client URL", - Description: "The URL of the monitoring client that is triggering this event. You can use templates", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "{{ .ExternalURL }}", - PropertyName: "client_url", - }, - { // New in 9.5. - Label: "Details", - Description: "A set of arbitrary key/value pairs that provide further detail about the incident.", - Element: ElementTypeKeyValueMap, - InputType: InputTypeText, - PropertyName: "details", - }, - { // New in 11.1 - Label: "URL", - Description: "The URL to send API requests to", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: alertingPagerduty.DefaultURL, - PropertyName: "url", - }, - }, - }, - { - Type: "victorops", - Name: "VictorOps", - Description: "Sends notifications to VictorOps", - Heading: "VictorOps settings", - Options: []NotifierOption{ - { - Label: "URL", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "VictorOps url", - PropertyName: "url", - Required: true, - Secure: true, - }, - { // New in 8.0. - Label: "Message Type", - Element: ElementTypeSelect, - PropertyName: "messageType", - SelectOptions: []SelectOption{ - { - Value: "CRITICAL", - Label: "CRITICAL"}, - { - Value: "WARNING", - Label: "WARNING", - }, - }, - }, - { // New in 9.3. - Label: "Title", - Element: ElementTypeTextArea, - InputType: InputTypeText, - Description: "Templated title to display", - PropertyName: "title", - Placeholder: alertingTemplates.DefaultMessageTitleEmbed, - }, - { // New in 9.3. - Label: "Description", - Element: ElementTypeTextArea, - InputType: InputTypeText, - Description: "Templated description of the message", - PropertyName: "description", - Placeholder: alertingTemplates.DefaultMessageEmbed, - }, - }, - }, - { - Type: "oncall", - Name: "Grafana IRM", - Description: "Sends alerts to Grafana IRM", - Heading: "Grafana IRM settings", - Options: []NotifierOption{ - { - Label: "URL", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "url", - Required: true, - }, - { - Label: "HTTP Method", - Element: ElementTypeSelect, - SelectOptions: []SelectOption{ - { - Value: "POST", - Label: "POST", - }, - { - Value: "PUT", - Label: "PUT", - }, - }, - PropertyName: "httpMethod", - }, - { - Label: "HTTP Basic Authentication - Username", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "username", - }, - { - Label: "HTTP Basic Authentication - Password", - Element: ElementTypeInput, - InputType: InputTypePassword, - PropertyName: "password", - Secure: true, - }, - { // New in 9.1 - Label: "Authorization Header - Scheme", - Description: "Optionally provide a scheme for the Authorization Request Header. Default is Bearer.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "authorization_scheme", - Placeholder: "Bearer", - }, - { // New in 9.1 - Label: "Authorization Header - Credentials", - Description: "Credentials for the Authorization Request header. Only one of HTTP Basic Authentication or Authorization Request Header can be set.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "authorization_credentials", - Secure: true, - }, - { // New in 8.0. TODO: How to enforce only numbers? - Label: "Max Alerts", - Description: "Max alerts to include in a notification. Remaining alerts in the same batch will be ignored above this number. 0 means no limit.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "maxAlerts", - }, - { // New in 9.3. - Label: "Title", - Description: "Templated title of the message.", - Element: ElementTypeTextArea, - InputType: InputTypeText, - PropertyName: "title", - Placeholder: alertingTemplates.DefaultMessageTitleEmbed, - }, - { // New in 9.3. - Label: "Message", - Description: "Custom message. You can use template variables.", - Element: ElementTypeTextArea, - PropertyName: "message", - Placeholder: alertingTemplates.DefaultMessageEmbed, - }, - }, - }, - { - Type: "pushover", - Name: "Pushover", - Description: "Sends HTTP POST request to the Pushover API", - Heading: "Pushover settings", - Options: []NotifierOption{ - { - Label: "API Token", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "Application token", - PropertyName: "apiToken", - Required: true, - Secure: true, - }, - { - Label: "User key(s)", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "comma-separated list", - PropertyName: "userKey", - Required: true, - Secure: true, - }, - { - Label: "Device(s) (optional)", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "comma-separated list; leave empty to send to all devices", - PropertyName: "device", - }, - { - Label: "Alerting priority", - Element: ElementTypeSelect, - SelectOptions: pushoverPriorityOptions, - PropertyName: "priority", - }, - { - Label: "OK priority", - Element: ElementTypeSelect, - SelectOptions: pushoverPriorityOptions, - PropertyName: "okPriority", - }, - { - Description: "How often (in seconds) the Pushover servers will send the same alerting or OK notification to the user.", - Label: "Retry (Only used for Emergency Priority)", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "minimum 30 seconds", - PropertyName: "retry", - }, - { - Description: "How many seconds the alerting or OK notification will continue to be retried.", - Label: "Expire (Only used for Emergency Priority)", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "maximum 10800 seconds", - PropertyName: "expire", - }, - { - Label: "Alerting sound", - Element: ElementTypeSelect, - SelectOptions: pushoverSoundOptions, - PropertyName: "sound", - }, - { - Label: "OK sound", - Element: ElementTypeSelect, - SelectOptions: pushoverSoundOptions, - PropertyName: "okSound", - }, - { // New in 9.3. - Label: "Title", - Element: ElementTypeTextArea, - InputType: InputTypeText, - Placeholder: alertingTemplates.DefaultMessageTitleEmbed, - PropertyName: "title", - }, - { // New in 8.0. - Label: "Message", - Element: ElementTypeTextArea, - Placeholder: alertingTemplates.DefaultMessageEmbed, - PropertyName: "message", - }, - }, - }, - { - Type: "slack", - Name: "Slack", - Description: "Sends notifications to Slack", - Heading: "Slack settings", - Options: []NotifierOption{ - { - Label: "Recipient", - Element: ElementTypeInput, - InputType: InputTypeText, - Description: "Specify channel, private group, or IM channel (can be an encoded ID or a name) - required unless you provide a webhook", - PropertyName: "recipient", - Required: true, - DependsOn: "url", - }, - // Logically, this field should be required when not using a webhook, since the Slack API needs a token. - // However, since the UI doesn't allow to say that a field is required or not depending on another field, - // we've gone with the compromise of making this field optional and instead return a validation error - // if it's necessary and missing. - { - Label: "Token", - Element: ElementTypeInput, - InputType: InputTypeText, - Description: "Provide a Slack API token (starts with \"xoxb\") - required unless you provide a webhook", - PropertyName: "token", - Secure: true, - Required: true, - DependsOn: "url", - }, - { - Label: "Username", - Element: ElementTypeInput, - InputType: InputTypeText, - Description: "Set the username for the bot's message", - PropertyName: "username", - }, - { - Label: "Icon emoji", - Element: ElementTypeInput, - InputType: InputTypeText, - Description: "Provide an emoji to use as the icon for the bot's message. Overrides the icon URL.", - PropertyName: "icon_emoji", - }, - { - Label: "Icon URL", - Element: ElementTypeInput, - InputType: InputTypeText, - Description: "Provide a URL to an image to use as the icon for the bot's message", - PropertyName: "icon_url", - }, - { - Label: "Mention Users", - Element: ElementTypeInput, - InputType: InputTypeText, - Description: "Mention one or more users (comma separated) when notifying in a channel, by ID (you can copy this from the user's Slack profile)", - PropertyName: "mentionUsers", - }, - { - Label: "Mention Groups", - Element: ElementTypeInput, - InputType: InputTypeText, - Description: "Mention one or more groups (comma separated) when notifying in a channel (you can copy this from the group's Slack profile URL)", - PropertyName: "mentionGroups", - }, - { - Label: "Mention Channel", - Element: ElementTypeSelect, - SelectOptions: []SelectOption{ - { - Value: "", - Label: "Disabled", - }, - { - Value: "here", - Label: "Every active channel member", - }, - { - Value: "channel", - Label: "Every channel member", - }, - }, - Description: "Mention whole channel or just active members when notifying", - PropertyName: "mentionChannel", - }, - { - Label: "Webhook URL", - Element: ElementTypeInput, - InputType: InputTypeText, - Description: "Optionally provide a Slack incoming webhook URL for sending messages, in this case the token isn't necessary", - Placeholder: "Slack incoming webhook URL", - PropertyName: "url", - Secure: true, - Required: true, - DependsOn: "token", - }, - { // New in 8.4. - Label: "Endpoint URL", - Element: ElementTypeInput, - InputType: InputTypeText, - Description: "Optionally provide a custom Slack message API endpoint for non-webhook requests, default is https://slack.com/api/chat.postMessage", - Placeholder: "Slack endpoint url", - PropertyName: "endpointUrl", - }, - { - Label: "Color", - Element: ElementTypeInput, - InputType: InputTypeText, - Description: "Templated color of the slack message", - Placeholder: alertingTemplates.DefaultMessageColor, - PropertyName: "color", - }, - { // New in 8.0. - Label: "Title", - Element: ElementTypeInput, - InputType: InputTypeText, - Description: "Templated title of the slack message", - PropertyName: "title", - Placeholder: `{{ template "slack.default.title" . }}`, - }, - { // New in 8.0. - Label: "Text Body", - Element: ElementTypeTextArea, - Description: "Body of the slack message", - PropertyName: "text", - Placeholder: `{{ template "slack.default.text" . }}`, - }, - }, - }, - { - Type: "sensugo", - Name: "Sensu Go", - Description: "Sends HTTP POST request to a Sensu Go API", - Heading: "Sensu Go Settings", - Options: []NotifierOption{ - { - Label: "Backend URL", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "http://sensu-api.local:8080", - PropertyName: "url", - Required: true, - }, - { - Label: "API Key", - Element: ElementTypeInput, - InputType: InputTypePassword, - Description: "API key to auth to Sensu Go backend", - PropertyName: "apikey", - Required: true, - Secure: true, - }, - { - Label: "Proxy entity name", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "default", - PropertyName: "entity", - }, - { - Label: "Check name", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "default", - PropertyName: "check", - }, - { - Label: "Handler", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "handler", - }, - { - Label: "Namespace", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "default", - PropertyName: "namespace", - }, - { // New in 8.0. - Label: "Message", - Element: ElementTypeTextArea, - Placeholder: alertingTemplates.DefaultMessageEmbed, - PropertyName: "message", - }, - }, - }, - { - Type: "teams", - Name: "Microsoft Teams", - Description: "Sends notifications using Incoming Webhook connector to Microsoft Teams", - Heading: "Teams settings", - Options: []NotifierOption{ - { - Label: "URL", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "Teams incoming webhook url", - PropertyName: "url", - Required: true, - }, - { - Label: "Title", - Element: ElementTypeTextArea, - InputType: InputTypeText, - Description: "Templated title of the Teams message.", - PropertyName: "title", - Placeholder: alertingTemplates.DefaultMessageTitleEmbed, - }, - { - Label: "Section Title", - Element: ElementTypeInput, - InputType: InputTypeText, - Description: "Section title for the Teams message. Leave blank for none.", - PropertyName: "sectiontitle", - }, - { // New in 8.0. - Label: "Message", - Element: ElementTypeTextArea, - Placeholder: alertingTemplates.DefaultMessageEmbed, - PropertyName: "message", - }, - }, - }, - { - Type: "telegram", - Name: "Telegram", - Description: "Sends notifications to Telegram", - Heading: "Telegram API settings", - Options: []NotifierOption{ - { - Label: "BOT API Token", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "Telegram BOT API Token", - PropertyName: "bottoken", - Required: true, - Secure: true, - }, - { - Label: "Chat ID", - Element: ElementTypeInput, - InputType: InputTypeText, - Description: "Integer Telegram Chat Identifier", - PropertyName: "chatid", - Required: true, - }, - { - Label: "Message Thread ID", - Element: ElementTypeInput, - InputType: InputTypeText, - Description: "Integer Telegram Message Thread Identifier", - PropertyName: "message_thread_id", - Required: false, - ValidationRule: "-?[0-9]{1,10}", - }, - { // New in 8.0. - Label: "Message", - Element: ElementTypeTextArea, - Placeholder: alertingTemplates.DefaultMessageEmbed, - PropertyName: "message", - }, - { - Label: "Parse Mode", - Element: ElementTypeSelect, - SelectOptions: []SelectOption{ - { - Value: "None", - Label: "None", - }, - { - Value: "HTML", - Label: "HTML", - }, - { - Value: "Markdown", - Label: "Markdown", - }, - { - Value: "MarkdownV2", - Label: "Markdown V2", - }, - }, - Description: `Mode for parsing entities in the message text. Default is 'HTML'`, - PropertyName: "parse_mode", - }, - { - Label: "Disable Web Page Preview", - Description: "Disables link previews for links in this message", - Element: ElementTypeCheckbox, - PropertyName: "disable_web_page_preview", - }, - { - Label: "Protect Content", - Description: "Protects the contents of the sent message from forwarding and saving", - Element: ElementTypeCheckbox, - PropertyName: "protect_content", - }, - { - Label: "Disable Notification", - Description: "Sends the message silently. Users will receive a notification with no sound.", - Element: ElementTypeCheckbox, - PropertyName: "disable_notification", - }, - }, - }, - { - Type: "webhook", - Name: "Webhook", - Description: "Sends HTTP POST request to a URL", - Heading: "Webhook settings", - Options: []NotifierOption{ - { - Label: "URL", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "url", - Required: true, - }, - { - Label: "HTTP Method", - Element: ElementTypeSelect, - SelectOptions: []SelectOption{ - { - Value: "POST", - Label: "POST", - }, - { - Value: "PUT", - Label: "PUT", - }, - }, - PropertyName: "httpMethod", - }, - { - Label: "HTTP Basic Authentication - Username", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "username", - }, - { - Label: "HTTP Basic Authentication - Password", - Element: ElementTypeInput, - InputType: InputTypePassword, - PropertyName: "password", - Secure: true, - }, - { // New in 9.1 - Label: "Authorization Header - Scheme", - Description: "Optionally provide a scheme for the Authorization Request Header. Default is Bearer.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "authorization_scheme", - Placeholder: "Bearer", - }, - { // New in 9.1 - Label: "Authorization Header - Credentials", - Description: "Credentials for the Authorization Request header. Only one of HTTP Basic Authentication or Authorization Request Header can be set.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "authorization_credentials", - Secure: true, - }, - { // New in 12.0. - Label: "Extra Headers", - Description: "Optionally provide extra headers to be used in the request.", - Element: ElementTypeKeyValueMap, - InputType: InputTypeText, - PropertyName: "headers", - }, - { // New in 8.0. TODO: How to enforce only numbers? - Label: "Max Alerts", - Description: "Max alerts to include in a notification. Remaining alerts in the same batch will be ignored above this number. 0 means no limit.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "maxAlerts", - }, - { // New in 9.3. - Label: "Title", - Description: "Templated title of the message.", - Element: ElementTypeTextArea, - InputType: InputTypeText, - PropertyName: "title", - Placeholder: alertingTemplates.DefaultMessageTitleEmbed, - }, - { // New in 9.3. - Label: "Message", - Description: "Templated message to be used in the payload's \"message\" field.", - Element: ElementTypeTextArea, - PropertyName: "message", - Placeholder: alertingTemplates.DefaultMessageEmbed, - }, - { // New in 12.0. - Label: "Custom Payload", - Description: "Optionally provide a templated payload. Overrides 'Message' and 'Title' field.", - Element: ElementTypeSubform, - PropertyName: "payload", - SubformOptions: []NotifierOption{ - { - Label: "Payload Template", - Description: "Custom payload template.", - Element: ElementTypeTextArea, - PropertyName: "template", - Placeholder: `{{ template "webhook.default.payload" . }}`, - Required: true, - }, - { - Label: "Payload Variables", - Description: "Optionally provide a variables to be used in the payload template. They will be available in the template as `.Vars.`.", - Element: ElementTypeKeyValueMap, - InputType: InputTypeText, - PropertyName: "vars", - }, - }, - }, - - { - Label: "TLS", - PropertyName: "tlsConfig", - Description: "TLS configuration options", - Element: ElementTypeSubform, - SubformOptions: tlsSubformOptions(), - }, - { - Label: "HMAC Signature", - PropertyName: "hmacConfig", - Description: "HMAC signature configuration options", - Element: ElementTypeSubform, - SubformOptions: []NotifierOption{ - { - Label: "Secret", - Element: ElementTypeInput, - Description: "", - InputType: InputTypeText, - PropertyName: "secret", - Required: true, - Secure: true, - }, - { - Label: "Header", - Element: ElementTypeInput, - Description: "The header in which the HMAC signature will be included.", - InputType: InputTypeText, - PropertyName: "header", - Placeholder: "X-Grafana-Alerting-Signature", - Required: false, - Secure: false, - }, - { - Label: "Timestamp header", - Element: ElementTypeInput, - Description: "If set, the timestamp will be included in the HMAC signature. The value should be the name of the header to use.", - InputType: InputTypeText, - PropertyName: "timestampHeader", - Required: false, - Secure: false, - }, - }, - }, - commonHttpClientOption(), // New in 12.1. - }, - }, - { - Type: "wecom", - Name: "WeCom", - Description: "Send alerts generated by Grafana to WeCom", - Heading: "WeCom settings", - Options: []NotifierOption{ - { - Label: "Webhook URL", - Description: "Required if using GroupRobot", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxxxxxx", - PropertyName: "url", - Secure: true, - Required: true, - DependsOn: "secret", - }, - { - Label: "Agent ID", - Description: "Required if using APIAPP, see https://work.weixin.qq.com/wework_admin/frame#apps create ApiApp", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "1000002", - PropertyName: "agent_id", - Required: true, - DependsOn: "url", - }, - { - Label: "Corp ID", - Description: "Required if using APIAPP, see https://work.weixin.qq.com/wework_admin/frame#profile", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "wwxxxxxxxxx", - PropertyName: "corp_id", - Required: true, - DependsOn: "url", - }, - { - Label: "Secret", - Description: "Required if using APIAPP", - Element: ElementTypeInput, - InputType: InputTypePassword, - Placeholder: "secret", - PropertyName: "secret", - Secure: true, - Required: true, - DependsOn: "url", - }, - { - Label: "Message Type", - Element: ElementTypeSelect, - PropertyName: "msgtype", - SelectOptions: []SelectOption{ - { - Value: "text", - Label: "Text", - }, - { - Value: "markdown", - Label: "Markdown", - }, - }, - Placeholder: "Text", - }, - { - Label: "Message", - Description: "Custom WeCom message. You can use template variables.", - Element: ElementTypeTextArea, - Placeholder: alertingTemplates.DefaultMessageEmbed, - PropertyName: "message", - }, - { // New in 9.1. - Label: "Title", - Element: ElementTypeTextArea, - InputType: InputTypeText, - Description: "Templated title of the message", - PropertyName: "title", - Placeholder: alertingTemplates.DefaultMessageTitleEmbed, - }, - { - Label: "To User", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "@all", - PropertyName: "touser", - }, - }, - }, - { - Type: "prometheus-alertmanager", - Name: "Alertmanager", - Description: "Sends notifications to Alertmanager", - Heading: "Alertmanager Settings", - Options: []NotifierOption{ - { - Label: "URL", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "http://localhost:9093", - PropertyName: "url", - Required: true, - }, - { - Label: "Basic Auth User", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "basicAuthUser", - }, - { - Label: "Basic Auth Password", - Element: ElementTypeInput, - InputType: InputTypePassword, - PropertyName: "basicAuthPassword", - Secure: true, - }, - }, - }, - { - Type: "discord", - Name: "Discord", - Heading: "Discord settings", - Description: "Sends notifications to Discord", - Options: []NotifierOption{ - { - Label: "Title", - Description: "Templated title of the message", - Element: ElementTypeTextArea, - InputType: InputTypeText, - Placeholder: alertingTemplates.DefaultMessageTitleEmbed, - PropertyName: "title", - }, - { - Label: "Message Content", - Description: "Mention a group using @ or a user using <@ID> when notifying in a channel", - Element: ElementTypeTextArea, - InputType: InputTypeText, - Placeholder: alertingTemplates.DefaultMessageEmbed, - PropertyName: "message", - }, - { - Label: "Webhook URL", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "Discord webhook URL", - PropertyName: "url", - Required: true, - Secure: true, - }, - { - Label: "Avatar URL", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "avatar_url", - }, - { - Label: "Use Discord's Webhook Username", - Description: "Use the username configured in Discord's webhook settings. Otherwise, the username will be 'Grafana'", - Element: ElementTypeCheckbox, - PropertyName: "use_discord_username", - }, - }, - }, - { - Type: "googlechat", - Name: "Google Chat", - Description: "Sends notifications to Google Chat via webhooks based on the official JSON message format", - Heading: "Google Chat settings", - Options: []NotifierOption{ - { - Label: "URL", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "Google Chat incoming webhook url", - PropertyName: "url", - Required: true, - Secure: true, - }, - { - Label: "Title", - Description: "Templated title of the message", - Element: ElementTypeTextArea, - InputType: InputTypeText, - Placeholder: alertingTemplates.DefaultMessageTitleEmbed, - PropertyName: "title", - }, - { - Label: "Message", - Element: ElementTypeTextArea, - Placeholder: alertingTemplates.DefaultMessageEmbed, - PropertyName: "message", - }, - }, - }, - { - Type: "LINE", - Name: "LINE", - Description: "Send notifications to LINE notify", - Heading: "LINE notify settings", - Options: []NotifierOption{ - { - Label: "Token", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "LINE notify token key", - PropertyName: "token", - Required: true, - Secure: true, - }, - { // New in 9.3 - Label: "Title", - Element: ElementTypeTextArea, - InputType: InputTypeText, - Description: "Templated title of the message", - PropertyName: "title", - Placeholder: alertingTemplates.DefaultMessageTitleEmbed, - }, - { // New in 9.3 - Label: "Description", - Element: ElementTypeTextArea, - InputType: InputTypeText, - Description: "Templated description of the message", - PropertyName: "description", - Placeholder: alertingTemplates.DefaultMessageEmbed, - }, - }, - }, - { - Type: "threema", - Name: "Threema Gateway", - Description: "Sends notifications to Threema using Threema Gateway (Basic IDs)", - Heading: "Threema Gateway settings", - Info: "Notifications can be configured for any Threema Gateway ID of type \"Basic\". End-to-End IDs are not currently supported." + - "The Threema Gateway ID can be set up at https://gateway.threema.ch/.", - Options: []NotifierOption{ - { - Label: "Gateway ID", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "*3MAGWID", - Description: "Your 8 character Threema Gateway Basic ID (starting with a *).", - PropertyName: "gateway_id", - Required: true, - ValidationRule: "\\*[0-9A-Z]{7}", - }, - { - Label: "Recipient ID", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "YOUR3MID", - Description: "The 8 character Threema ID that should receive the alerts.", - PropertyName: "recipient_id", - Required: true, - ValidationRule: "[0-9A-Z]{8}", - }, - { - Label: "API Secret", - Element: ElementTypeInput, - InputType: InputTypeText, - Description: "Your Threema Gateway API secret.", - PropertyName: "api_secret", - Required: true, - Secure: true, - }, - { // New in 9.3 - Label: "Title", - Element: ElementTypeTextArea, - InputType: InputTypeText, - Description: "Templated title of the message.", - PropertyName: "title", - Placeholder: alertingTemplates.DefaultMessageTitleEmbed, - }, - { // New in 9.3 - Label: "Description", - Element: ElementTypeTextArea, - InputType: InputTypeText, - Description: "Templated description of the message.", - PropertyName: "description", - Placeholder: alertingTemplates.DefaultMessageEmbed, - }, - }, - }, - { - Type: "mqtt", - Name: "MQTT", - Description: "Sends notifications to an MQTT broker", - Heading: "MQTT settings", - Info: "The MQTT notifier sends messages to an MQTT broker. The message is sent to the topic specified in the configuration. ", - Options: []NotifierOption{ - { - Label: "Broker URL", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "tcp://localhost:1883", - Description: "The URL of the MQTT broker.", - PropertyName: "brokerUrl", - Required: true, - }, - { - Label: "Topic", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "grafana/alerts", - Description: "The topic to which the message will be sent.", - PropertyName: "topic", - Required: true, - }, - { - Label: "Message format", - Element: ElementTypeSelect, - SelectOptions: []SelectOption{ - { - Value: alertingMqtt.MessageFormatJSON, - Label: "json", - }, - { - Value: alertingMqtt.MessageFormatText, - Label: "text", - }, - }, - InputType: InputTypeText, - Placeholder: "json", - Description: "If set to 'json', the notification message is the default JSON payload, and the Message field sets only the message field in the payload. If set to 'text', the Message field defines the entire payload. The default is 'json'.", - PropertyName: "messageFormat", - Required: false, - }, - { - Label: "Client ID", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "", - Description: "The client ID to use when connecting to the MQTT broker. If blank, a random client ID is used.", - PropertyName: "clientId", - Required: false, - }, - { - Label: "Message", - Element: ElementTypeTextArea, - Description: "In 'json' Message format, sets the message field of the default JSON payload. In 'text' Message format, defines the entire payload.", - Placeholder: alertingTemplates.DefaultMessageEmbed, - PropertyName: "message", - }, - { - Label: "Username", - Description: "The username to use when connecting to the MQTT broker.", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "", - PropertyName: "username", - Required: false, - }, - { - Label: "Password", - Description: "The password to use when connecting to the MQTT broker.", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "", - PropertyName: "password", - Required: false, - Secure: true, - }, - { - Label: "QoS", - Element: ElementTypeSelect, - SelectOptions: []SelectOption{ - { - Value: "0", - Label: "At most once (0)", - }, - { - Value: "1", - Label: "At least once (1)", - }, - { - Value: "2", - Label: "Exactly once (2)", - }, - }, - Description: "The quality of service to use when sending the message.", - PropertyName: "qos", - Required: false, - }, - { - Label: "Retain", - Description: "If set to true, the message will be retained by the broker.", - Element: ElementTypeCheckbox, - PropertyName: "retain", - Required: false, - }, - { - Label: "TLS", - PropertyName: "tlsConfig", - Description: "TLS configuration options", - Element: ElementTypeSubform, - SubformOptions: []NotifierOption{ - { - Label: "Disable certificate verification", - Element: ElementTypeCheckbox, - Description: "Do not verify the broker's certificate chain and host name.", - PropertyName: "insecureSkipVerify", - Required: false, - }, - { - Label: "CA Certificate", - Element: ElementTypeTextArea, - Description: "Certificate in PEM format to use when verifying the broker's certificate chain.", - InputType: InputTypeText, - PropertyName: "caCertificate", - Required: false, - Secure: true, - }, - { - Label: "Client Certificate", - Element: ElementTypeTextArea, - Description: "Client certificate in PEM format to use when connecting to the broker.", - InputType: InputTypeText, - PropertyName: "clientCertificate", - Required: false, - Secure: true, - }, - { - Label: "Client Key", - Element: ElementTypeTextArea, - Description: "Client key in PEM format to use when connecting to the broker.", - InputType: InputTypeText, - PropertyName: "clientKey", - Required: false, - Secure: true, - }, - }, - }, - }, - }, - { - Type: "opsgenie", - Name: "OpsGenie", - Description: "Sends notifications to OpsGenie", - Heading: "OpsGenie settings", - Options: []NotifierOption{ - { - Label: "API Key", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "OpsGenie API Key", - PropertyName: "apiKey", - Required: true, - Secure: true, - }, - { - Label: "Alert API URL", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "https://api.opsgenie.com/v2/alerts", - PropertyName: "apiUrl", - Required: true, - }, - { - Label: "Message", - Description: "Alert text limited to 130 characters.", - Element: ElementTypeTextArea, - InputType: InputTypeText, - Placeholder: alertingTemplates.DefaultMessageTitleEmbed, - PropertyName: "message", - }, - { - Label: "Description", - Description: "A description of the incident.", - Element: ElementTypeTextArea, - PropertyName: "description", - }, - { - Label: "Auto close incidents", - Element: ElementTypeCheckbox, - Description: "Automatically close alerts in OpsGenie once the alert goes back to ok.", - PropertyName: "autoClose", - }, { - Label: "Override priority", - Element: ElementTypeCheckbox, - Description: "Allow the alert priority to be set using the og_priority label.", - PropertyName: "overridePriority", - }, - { - Label: "Send notification tags as", - Element: ElementTypeSelect, - SelectOptions: []SelectOption{ - { - Value: alertingOpsgenie.SendTags, - Label: "Tags", - }, - { - Value: alertingOpsgenie.SendDetails, - Label: "Extra Properties", - }, - { - Value: alertingOpsgenie.SendBoth, - Label: "Tags & Extra Properties", - }, - }, - Description: "Send the common annotations to Opsgenie as either Extra Properties, Tags or both", - PropertyName: "sendTagsAs", - }, - // New in 10.3 - { - Label: "Responders", - PropertyName: "responders", - Description: "If the API key belongs to a team, this field is ignored.", - Element: ElementSubformArray, - SubformOptions: []NotifierOption{ - { - Label: "Type", - Description: fmt.Sprintf("%s or a template", strings.Join(alertingOpsgenie.SupportedResponderTypes, ", ")), - Element: ElementTypeInput, - Required: true, - PropertyName: "type", - }, - { - Label: "Name", - Element: ElementTypeInput, - Description: "Name of the responder. Must be specified if ID and Username are empty or if the type is 'teams'.", - PropertyName: "name", - }, - { - Label: "ID", - Element: ElementTypeInput, - Description: "ID of the responder. Must be specified if name and Username are empty.", - PropertyName: "id", - }, - { - Label: "Username", - Element: ElementTypeInput, - Description: "User name of the responder. Must be specified if ID and Name are empty.", - PropertyName: "username", - }, - }, - }, - }, - }, - { - Type: "webex", - Name: "Cisco Webex Teams", - Description: "Sends notifications to Cisco Webex Teams", - Heading: "Webex settings", - Info: "Notifications can be configured for any Cisco Webex Teams", - Options: []NotifierOption{ - { - Label: "Cisco Webex API URL", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "https://api.ciscospark.com/v1/messages", - Description: "API endpoint at which we'll send webhooks to.", - PropertyName: "api_url", - }, - { - Label: "Room ID", - Description: "The room ID to send messages to.", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "GMtOWY0ZGJkNzMyMGFl", - PropertyName: "room_id", - Required: true, - }, - { - Label: "Bot Token", - Description: "Non-expiring access token of the bot that will post messages on our behalf.", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: `GMtOWY0ZGJkNzMyMGFl-12535454-123213`, - PropertyName: "bot_token", - Secure: true, - Required: true, - }, - { - Label: "Notification Template", - Description: "Notification template to use. Markdown is supported.", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: `{{ template "default.message" . }}`, - PropertyName: "message", - }, - }, - }, - { // Since Grafana 11.1 - Type: "sns", - Name: "AWS SNS", - Description: "Sends notifications to AWS Simple Notification Service", - Heading: "Webex settings", - Options: []NotifierOption{ - { - Label: "The Amazon SNS API URL", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "", - PropertyName: "api_url", - }, - { - Label: "SigV4 Authentication", - Description: "Configures AWS's Signature Verification 4 signing process to sign requests", - Element: ElementTypeSubform, - PropertyName: "sigv4", - SubformOptions: []NotifierOption{ - { - Label: "Region", - Description: "The AWS region. If blank, the region from the default credentials chain is used.", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "", - PropertyName: "region", - }, - { - Label: "Access Key", - Description: "The AWS API access key.", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "", - PropertyName: "access_key", - Secure: true, - }, - { - Label: "Secret Key", - Description: "The AWS API secret key.", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "", - PropertyName: "secret_key", - Secure: true, - }, - { - Label: "Profile", - Description: "Named AWS profile used to authenticate", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "", - PropertyName: "profile", - }, - { - Label: "Role ARN", - Description: "AWS Role ARN, an alternative to using AWS API keys", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "", - PropertyName: "role_arn", - }, - }, - }, - { - Label: "SNS topic ARN", - Description: "If you don't specify this value, you must specify a value for the phone_number or target_arn. If you are using a FIFO SNS topic you should set a message group interval longer than 5 minutes to prevent messages with the same group key being deduplicated by the SNS default deduplication window.", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "", - PropertyName: "topic_arn", - }, - { - Label: "Phone number", - Description: "Phone number if message is delivered via SMS in E.164 format. If you don't specify this value, you must specify a value for the topic_arn or target_arn", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: ``, - PropertyName: "phone_number", - Secure: false, - }, - { - Label: "Target ARN", - Description: "The mobile platform endpoint ARN if message is delivered via mobile notifications. If you don't specify this value, you must specify a value for the topic_arn or phone_number", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: ``, - PropertyName: "target_arn", - }, - { - Label: "Subject", - Element: ElementTypeTextArea, - InputType: InputTypeText, - Description: "Optional subject. By default, this field uses the default title template and can be customized with templates and custom messages. It cannot be an empty string", - PropertyName: "subject", - Placeholder: alertingTemplates.DefaultMessageTitleEmbed, - }, - { - Label: "Message", - Description: "Optional message. By default, this field uses the default message template and can be customized with templates and custom messages", - Element: ElementTypeTextArea, - PropertyName: "message", - Placeholder: alertingTemplates.DefaultMessageEmbed, - }, - { - Label: "Attributes", - Description: "SNS message attributes", - Element: ElementTypeKeyValueMap, - InputType: InputTypeText, - PropertyName: "attributes", - }, - }, - }, - { // Since Grafana 11.6 - Type: "jira", - Name: "Jira", - Description: "Creates Jira issues from alerts", - Heading: "Jira settings", - Options: []NotifierOption{ - { - Label: "API URL of Jira instance, including version of API", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "https://grafana.atlassian.net/rest/api/3", - PropertyName: "api_url", - Description: "Supported v2 or v3 APIs", - Required: true, - }, - { - Label: "HTTP Basic Authentication - Username", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "user", - Description: "Username to use for Jira authentication.", - Secure: true, - Required: false, - }, - { - Label: "HTTP Basic Authentication - Password", - Element: ElementTypeInput, - InputType: InputTypePassword, - PropertyName: "password", - // Go to https://id.atlassian.com/manage-profile/security/api-tokens to obtain a token. - Description: "Password to use for Jira authentication.", - Secure: true, - Required: false, - }, - { - Label: "Authorization Header - Personal Access Token", - Element: ElementTypeInput, - InputType: InputTypePassword, - PropertyName: "api_token", - // Go to https://confluence.atlassian.com/enterprise/using-personal-access-tokens-1026032365.html for how to obtain a token. - Description: "Personal Access Token that is used as a bearer authorization header.", - Secure: true, - Required: false, - }, - { - Label: "Project Key", - Description: "The project key associated with the relevant Jira project", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "Grafana", - PropertyName: "project", - Required: true, - }, - { - Label: "Issue Type", - Description: "The type of the Jira issue (e.g., Bug, Task, Story). You can use templates to customize this field.", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "Task", - Required: true, - PropertyName: "issue_type", - }, - { - Label: "Summary", - Description: fmt.Sprintf("The summary of the Jira issue. You can use templates to customize this field. Maximum length is %d characters.", jira.MaxSummaryLenRunes), - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: jira.DefaultSummary, - PropertyName: "summary", - }, - { - Label: "Description", - Description: fmt.Sprintf("The description of the Jira issue. You can use templates to customize this field. Maximum length is %d characters.", jira.MaxDescriptionLenRunes), - Element: ElementTypeTextArea, - InputType: InputTypeText, - Placeholder: jira.DefaultDescription, - PropertyName: "description", - }, - { - Label: "Labels", - Description: "Labels to assign to the Jira issue. You can use templates to customize this field.", - Element: ElementStringArray, - Placeholder: "", - PropertyName: "labels", - }, - { - Label: "Priority", - Description: "The priority of the Jira issue (e.g., High, Medium, Low). You can use templates to customize this field.", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: jira.DefaultPriority, - PropertyName: "priority", - Required: false, - }, - { - Label: "Resolve Transition", - Description: `Name of the workflow transition to resolve an issue. The target status must have the category "done". If not set, the issue will not be resolved.`, - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "", - PropertyName: "resolve_transition", - Required: false, - }, - { - Label: "Reopen Transition", - Description: `Name of the workflow transition to resolve an issue. The target status must not have the category "done". If not set, the issue will not be reopened.`, - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "", - PropertyName: "reopen_transition", - Required: false, - }, - { - Label: "Reopen Duration", - Description: "Reopen the issue when it is not older than this value in minutes. Otherwise, create a new issue.", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "10m", - PropertyName: "reopen_duration", - }, - { - Label: "\"Won't fix\" Transition", - Description: `If reopen transition is defined, ignore issues with that resolution.`, - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "", - PropertyName: "wont_fix_resolution", - Required: false, - }, - { - Label: "Custom field ID for deduplication", - Description: "Id of the custom field where the deduplication key should be stored. Otherwise, it is added to labels in format 'ALERT($KEY).'", - Element: ElementTypeInput, - InputType: InputTypeText, - Placeholder: "10000", - ValidationRule: "^[0-9]+$", - PropertyName: "dedup_key_field", - }, - { - Label: "Custom Field Data", - Description: "Custom field data to set on the Jira issue.", - Element: ElementTypeKeyValueMap, - InputType: InputTypeText, - Placeholder: "", - PropertyName: "fields", - }, - }, - }, - } -} - -// GetSecretKeysForContactPointType returns settings keys of contact point of the given type that are expected to be secrets. Returns error is contact point type is not known. -func GetSecretKeysForContactPointType(contactPointType string, version NotifierVersion) ([]string, error) { - var notifiers []*NotifierPlugin - if version == V1 { - notifiers = GetAvailableNotifiers() - } - if version == V0mimir1 { - notifiers = getAvailableV0mimir1Notifiers() - } - if version == V0mimir2 { - notifiers = getAvailableV0mimir2Notifiers() - } - for _, n := range notifiers { - if strings.EqualFold(n.Type, contactPointType) { - return getSecretFields("", n.Options), nil - } - } - return nil, fmt.Errorf("no secrets configured for type '%s' of version %s", contactPointType, version) -} - -func getSecretFields(parentPath string, options []NotifierOption) []string { - var secureFields []string - for _, field := range options { - name := field.PropertyName - if parentPath != "" { - name = parentPath + "." + name - } - if field.Secure { - secureFields = append(secureFields, name) - continue - } - if len(field.SubformOptions) > 0 { - secureFields = append(secureFields, getSecretFields(name, field.SubformOptions)...) - } - } - return secureFields -} - -// ConfigForIntegrationType returns the config for the given integration type. Returns error is integration type is not known. -func ConfigForIntegrationType(contactPointTypeOrAlternateType string) (NotifierPluginVersion, error) { - notifiers := GetAvailableNotifiersV2() - for n := range notifiers { - if strings.EqualFold(n.Type, contactPointTypeOrAlternateType) { - return n.GetCurrentVersion(), nil - } - for _, version := range n.Versions { - if version.TypeAlias == "" { - continue - } - if strings.EqualFold(version.TypeAlias, contactPointTypeOrAlternateType) { - return version, nil - } - } - } - return NotifierPluginVersion{}, fmt.Errorf("unknown integration type '%s'", contactPointTypeOrAlternateType) -} - -func GetAvailableNotifiersV2() iter.Seq[*VersionedNotifierPlugin] { - m := make(map[string]*VersionedNotifierPlugin, 24) // we support 24 notifier types - add := func(n []*NotifierPlugin, version NotifierVersion) { - for _, n := range n { - pl, ok := m[n.Type] - if !ok { - pl = &VersionedNotifierPlugin{ - Type: n.Type, - Name: n.Name, - Description: n.Description, - Heading: n.Heading, - Info: n.Info, - CurrentVersion: version, - Versions: make([]NotifierPluginVersion, 0, 2), // usually, there are 2 versions per type - } - m[n.Type] = pl - } - pl.Versions = append(pl.Versions, NotifierPluginVersion{ - Version: version, - // we allow users to create only v1 notifiers - CanCreate: version == V1, - Options: n.Options, - TypeAlias: n.TypeAlias, - Plugin: pl, - }) - } - } - add(GetAvailableNotifiers(), V1) - add(getAvailableV0mimir2Notifiers(), V0mimir2) - add(getAvailableV0mimir1Notifiers(), V0mimir1) - return maps.Values(m) -} diff --git a/pkg/services/ngalert/notifier/channels_config/available_channels_mimir.go b/pkg/services/ngalert/notifier/channels_config/available_channels_mimir.go deleted file mode 100644 index e8c89659f57..00000000000 --- a/pkg/services/ngalert/notifier/channels_config/available_channels_mimir.go +++ /dev/null @@ -1,1572 +0,0 @@ -package channels_config - -import ( - promCfg "github.com/prometheus/alertmanager/config" -) - -func getAvailableV0mimir1Notifiers() []*NotifierPlugin { - return []*NotifierPlugin{ - getDiscordMimirNotifier(), - getEmailMimirNotifier(), - getPagerDutyMimirNotifier(), - getSlackMimirNotifier(), - getWebhookMimirNotifier(), - getOpsGenieMimirNotifier(), - getWeChatMimirNotifier(), - getPushoverMimirNotifier(), - getVictorOpsMimirNotifier(), - getAmazonSNSMimirNotifier(), - getTelegramMimirNotifier(), - getWebexMimirNotifier(), - getMicrosoftTeamsMimirNotifier(), - getJiraMimirNotifier(), - } -} - -func getAvailableV0mimir2Notifiers() []*NotifierPlugin { - return []*NotifierPlugin{ - getMicrosoftTeamsV2MimirNotifier(), - } -} - -func getDiscordMimirNotifier() *NotifierPlugin { - return &NotifierPlugin{ - Type: "discord", - Name: "Discord", - Description: "Sends notifications to Discord", - Heading: "Discord settings", - Options: []NotifierOption{ - { - Label: "Webhook URL", - Placeholder: "Discord webhook URL", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "webhook_url", - Required: true, - Secure: true, - }, - { - Label: "Title", - Description: "Templated title of the message", - Placeholder: promCfg.DefaultDiscordConfig.Title, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "title", - }, - { - Label: "Message Content", - Description: "Mention a group using @ or a user using <@ID> when notifying in a channel", - Placeholder: promCfg.DefaultDiscordConfig.Message, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "message", - }, - v0HttpConfigOption(), - }, - } -} - -func getEmailMimirNotifier() *NotifierPlugin { - return &NotifierPlugin{ - Type: "email", - Name: "Email", - Description: "Send notification over SMTP", - Heading: "Email settings", - Info: "This integration does not use Grafana template", - Options: []NotifierOption{ - { - Label: "To", - Description: "The email address to send notifications to. You can enter multiple addresses using a \",\" separator. You can use templates to customize this field.", - Element: ElementTypeTextArea, - PropertyName: "to", - Required: true, - }, - { - Label: "From", - Description: "The sender address.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "from", - }, - { - Label: "SMTP host", - Description: "The SMTP host and port through which emails are sent.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "smarthost", - }, - { - Label: "Hello", - Description: "The hostname to identify to the SMTP server.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "hello", - }, - { - Label: "Username", - Description: "SMTP authentication information", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "auth_username", - }, - { - Label: "Password", - Description: "SMTP authentication information", - Element: ElementTypeInput, - InputType: InputTypePassword, - PropertyName: "auth_password", - Secure: true, - }, - { - Label: "Secret", - Description: "SMTP authentication information", - Element: ElementTypeInput, - InputType: InputTypePassword, - PropertyName: "auth_secret", - Secure: true, - }, - { - Label: "Identity", - Description: "SMTP authentication information", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "auth_identity", - }, - { - Label: "Require TLS", - Description: "The SMTP TLS requirement", - Element: ElementTypeCheckbox, - PropertyName: "require_tls", - }, - { - Label: "Email HTML body", - Description: "The HTML body of the email notification.", - Placeholder: promCfg.DefaultEmailConfig.HTML, - Element: ElementTypeTextArea, - PropertyName: "html", - }, - { - Label: "Email text body", - Description: "The text body of the email notification.", - Placeholder: promCfg.DefaultEmailConfig.Text, - Element: ElementTypeTextArea, - PropertyName: "text", - }, - { - Label: "Headers", - Description: "Further headers email header key/value pairs. Overrides any headers previously set by the notification implementation.", - Element: ElementTypeKeyValueMap, - PropertyName: "headers", - }, - v0TLSConfigOption("tls_config"), - }, - } -} - -func getPagerDutyMimirNotifier() *NotifierPlugin { - return &NotifierPlugin{ - Type: "pagerduty", - Name: "PagerDuty", - Description: "Send notifications to PagerDuty", - Heading: "PagerDuty settings", - Info: "", - Options: []NotifierOption{ - { - Label: "URL", - Description: "The URL to send API requests to", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "url", - Required: true, - }, - { - Label: "Routing key", - Description: "The PagerDuty integration key (when using PagerDuty integration type `Events API v2`)", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "routing_key", - Secure: true, - Required: true, - }, - { - Label: "Service key", - Description: "The PagerDuty integration key (when using PagerDuty integration type `Prometheus`).", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "service_key", - Secure: true, - Required: true, - }, - { - Label: "Client", - Description: "The client identification of the Alertmanager.", - Placeholder: promCfg.DefaultPagerdutyConfig.Client, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "client", - }, - { - Label: "Client URL", - Description: "A backlink to the sender of the notification.", - Placeholder: promCfg.DefaultPagerdutyConfig.ClientURL, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "client_url", - }, - { - Label: "Description", - Description: "A description of the incident.", - Placeholder: promCfg.DefaultPagerdutyConfig.Description, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "description", - }, - { - Label: "Details", - Description: "A set of arbitrary key/value pairs that provide further detail about the incident.", - Element: ElementTypeKeyValueMap, - PropertyName: "details", - }, - { - Label: "Images", - Description: "Images to attach to the incident.", - Element: ElementSubformArray, - PropertyName: "images", - SubformOptions: []NotifierOption{ - { - Label: "URL", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "href", - }, - { - Label: "Source", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "source", - }, - { - Label: "Alt", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "alt", - }, - }, - }, - { - Label: "Links", - Description: "Links to attach to the incident.", - Element: ElementSubformArray, - PropertyName: "links", - SubformOptions: []NotifierOption{ - { - Label: "URL", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "href", - }, - { - Label: "Text", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "text", - }, - }, - }, - { - Label: "Source", - Description: "Unique location of the affected system.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "source", - }, - { - Label: "Severity", - Description: "Severity of the incident.", - Placeholder: "error", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "severity", - }, - { - Label: "Class", - Description: "The class/type of the event.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "class", - }, - { - Label: "Component", - Description: "The part or component of the affected system that is broken.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "component", - }, - { - Label: "Group", - Description: "A cluster or grouping of sources.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "group", - }, - v0HttpConfigOption(), - }, - } -} - -func getSlackMimirNotifier() *NotifierPlugin { - return &NotifierPlugin{ - Type: "slack", - Name: "Slack", - Description: "Send notifications to Slack", - Heading: "Slack settings", - Info: "", - Options: []NotifierOption{ - { - Label: "Webhook URL", - Description: "The Slack webhook URL.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "api_url", - Secure: true, - Required: true, - }, - { - Label: "Channel", - Description: "The #channel or @user to send notifications to.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "channel", - }, - { - Label: "Username", - Placeholder: promCfg.DefaultSlackConfig.Username, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "username", - }, - { - Label: "Emoji icon", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "icon_emoji", - }, - { - Label: "Icon URL", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "icon_url", - }, - { - Label: "Names link", - Element: ElementTypeCheckbox, - PropertyName: "link_names", - }, - { - Label: "Callback ID", - Placeholder: promCfg.DefaultSlackConfig.CallbackID, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "callback_id", - }, - { - Label: "Color", - Placeholder: promCfg.DefaultSlackConfig.Color, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "color", - }, - { - Label: "Fallback", - Placeholder: promCfg.DefaultSlackConfig.Fallback, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "fallback", - }, - { - Label: "Footer", - Placeholder: promCfg.DefaultSlackConfig.Footer, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "footer", - }, - { - Label: "Markdown Fields", - Description: "An array of field names that should be formatted by markdown syntax.", - Element: ElementStringArray, - PropertyName: "mrkdwn_in", - }, - { - Label: "Pre-text", - Placeholder: promCfg.DefaultSlackConfig.Pretext, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "pretext", - }, - { - Label: "Short Fields", - Element: ElementTypeCheckbox, - PropertyName: "short_fields", - }, - { - Label: "Message body", - Placeholder: promCfg.DefaultSlackConfig.Text, - Element: ElementTypeTextArea, - PropertyName: "text", - }, - { - Label: "Title", - Placeholder: promCfg.DefaultSlackConfig.Title, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "title", - }, - { - Label: "Title Link", - Placeholder: promCfg.DefaultSlackConfig.TitleLink, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "title_link", - }, - { - Label: "Image URL", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "image_url", - }, - { - Label: "Thumbnail URL", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "thumb_url", - }, - { - Label: "Actions", - Element: ElementSubformArray, - PropertyName: "actions", - SubformOptions: []NotifierOption{ - { - Label: "Text", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "text", - Required: true, - }, - { - Label: "Type", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "type", - Required: true, - }, - { - Label: "URL", - Description: "Either url or name and value are mandatory.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "url", - }, - { - Label: "Name", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "name", - }, - { - Label: "Value", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "value", - }, - { - Label: "Confirm", - Element: ElementTypeSubform, - PropertyName: "confirm", - SubformOptions: []NotifierOption{ - { - Label: "Text", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "text", - Required: true, - }, - { - Label: "Dismiss text", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "dismiss_text", - }, - { - Label: "OK text", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "ok_text", - }, - { - Label: "Title", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "title", - }, - }, - }, - { - Label: "Style", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "style", - }, - }, - }, - { - Label: "Fields", - Element: ElementSubformArray, - PropertyName: "fields", - SubformOptions: []NotifierOption{ - { - Label: "Title", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "title", - Required: true, - }, - { - Label: "Value", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "value", - Required: true, - }, - { - Label: "Short", - Element: ElementTypeCheckbox, - PropertyName: "short", - }, - }, - }, - v0HttpConfigOption(), - }, - } -} - -func getWebhookMimirNotifier() *NotifierPlugin { - return &NotifierPlugin{ - Type: "webhook", - Name: "Webhook", - Description: "Send notifications to a webhook", - Heading: "Webhook settings", - Info: "", - Options: []NotifierOption{ - { - Label: "URL", - Description: "The endpoint to send HTTP POST requests to.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "url", - Secure: true, - Required: true, - }, - { - Label: "Max alerts", - Description: "The maximum number of alerts to include in a single webhook message. Alerts above this threshold are truncated. When leaving this at its default value of 0, all alerts are included.", - Element: ElementTypeInput, - InputType: "number", - PropertyName: "max_alerts", - ValidationRule: "(^\\d+$|^$)", - }, - { - Label: "Timeout", - Description: "The maximum time to wait for a webhook request to complete, before failing the request and allowing it to be retried. The default value of 0s indicates that no timeout should be applied. NOTE: This will have no effect if set higher than the group_interval.", - Placeholder: "Use duration format, for example: 1.2s, 100ms", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "timeout", - }, - v0HttpConfigOption(), - }, - } -} - -func getOpsGenieMimirNotifier() *NotifierPlugin { - return &NotifierPlugin{ - Type: "opsgenie", - Name: "OpsGenie", - Description: "Send notifications to OpsGenie", - Heading: "OpsGenie settings", - Info: "", - Options: []NotifierOption{ - { - Label: "API key", - Description: "The API key to use when talking to the OpsGenie API.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "api_key", - Secure: true, - Required: true, - }, - { - Label: "API URL", - Description: "The host to send OpsGenie API requests to.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "api_url", - Required: true, - }, - { - Label: "Message", - Description: "Alert text limited to 130 characters.", - Placeholder: promCfg.DefaultOpsGenieConfig.Message, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "message", - }, - { - Label: "Description", - Description: "A description of the incident.", - Placeholder: promCfg.DefaultOpsGenieConfig.Description, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "description", - }, - { - Label: "Source", - Description: "A backlink to the sender of the notification.", - Placeholder: promCfg.DefaultOpsGenieConfig.Source, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "source", - }, - { - Label: "Details", - Description: "A set of arbitrary key/value pairs that provide further detail about the incident.", - Element: ElementTypeKeyValueMap, - PropertyName: "details", - }, - { - Label: "Entity", - Description: "Optional field that can be used to specify which domain alert is related to.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "entity", - }, - { - Label: "Actions", - Description: "Comma separated list of actions that will be available for the alert.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "actions", - }, - { - Label: "Tags", - Description: "Comma separated list of tags attached to the notifications.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "tags", - }, - { - Label: "Note", - Description: "Additional alert note.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "note", - }, - { - Label: "Priority", - Description: "Priority level of alert. Possible values are P1, P2, P3, P4, and P5.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "priority", - }, - { - Label: "Update Alerts", - Description: "Whether to update message and description of the alert in OpsGenie if it already exists. By default, the alert is never updated in OpsGenie, the new message only appears in activity log.", - Element: ElementTypeCheckbox, - PropertyName: "update_alerts", - }, - { - Label: "Responders", - Description: "List of responders responsible for notifications.", - Element: ElementSubformArray, - PropertyName: "responders", - SubformOptions: []NotifierOption{ - { - Label: "Type", - Description: "\"team\", \"user\", \"escalation\" or schedule\".", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "type", - }, - { - Label: "ID", - Description: "Exactly one of these fields should be defined.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "id", - }, - { - Label: "Name", - Description: "Exactly one of these fields should be defined.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "name", - }, - { - Label: "Username", - Description: "Exactly one of these fields should be defined.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "username", - }, - }, - }, - v0HttpConfigOption(), - }, - } -} - -func getWeChatMimirNotifier() *NotifierPlugin { - return &NotifierPlugin{ - Type: "wechat", - Name: "WeChat", - Description: "Sends notifications to WeChat", - Heading: "WeChat settings", - Options: []NotifierOption{ - { - Label: "API URL", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "api_url", - }, - { - Label: "API Secret", - Description: "The API key to use when talking to the WeChat API", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "api_secret", - Secure: true, - }, - { - Label: "Corp ID", - Description: "The corp id for authentication", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "corp_id", - }, - { - Label: "Message", - Description: "API request data as defined by the WeChat API", - Placeholder: promCfg.DefaultWechatConfig.Message, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "message", - }, - { - Label: "Message type", - Description: "Type of the message type", - Element: ElementTypeSelect, - PropertyName: "message_type", - Placeholder: "text", - SelectOptions: []SelectOption{ - {Value: "text", Label: "Text"}, - {Value: "markdown", Label: "Markdown"}, - }, - }, - { - Label: "Agent ID", - Placeholder: promCfg.DefaultWechatConfig.AgentID, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "agent_id", - }, - { - Label: "To User", - Placeholder: promCfg.DefaultWechatConfig.ToUser, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "to_user", - }, - { - Label: "To Party", - Placeholder: promCfg.DefaultWechatConfig.ToParty, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "to_party", - }, - { - Label: "To Tag", - Placeholder: promCfg.DefaultWechatConfig.ToTag, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "to_tag", - }, - v0HttpConfigOption(), - }, - } -} - -func getPushoverMimirNotifier() *NotifierPlugin { - return &NotifierPlugin{ - Type: "pushover", - Name: "Pushover", - Description: "Send notifications to Pushover", - Heading: "Pushover settings", - Info: "", - Options: []NotifierOption{ - { - Label: "User key", - Description: "The recipient user’s user key.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "user_key", - Required: true, - Secure: true, - }, - { - Label: "Token", - Description: "Your registered application’s API token, see https://pushover.net/app", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "token", - Required: true, - Secure: true, - }, - { - Label: "Title", - Description: "Notification title.", - Placeholder: promCfg.DefaultPushoverConfig.Title, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "title", - }, - { - Label: "Message", - Description: "Notification message.", - Placeholder: promCfg.DefaultPushoverConfig.Message, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "message", - }, - { - Label: "URL", - Description: "A supplementary URL shown alongside the message.", - Placeholder: promCfg.DefaultPushoverConfig.URL, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "url", - }, - { - Label: "URL Title", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "url_title", - }, - { - Label: "Device", - Description: "Optional device to send notification to, see https://pushover.net/api#device", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "device", - }, - { - Label: "Sound", - Description: "Optional sound to use for notification, see https://pushover.net/api#sound", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "sound", - }, - { - Label: "Priority", - Description: "Priority, see https://pushover.net/api#priority", - Placeholder: promCfg.DefaultPushoverConfig.Priority, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "priority", - }, - { - Label: "Retry", - Description: "How often the Pushover servers will send the same notification to the user. Must be at least 30 seconds.", - Placeholder: "1m", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "retry", - }, - { - Label: "Expire", - Description: "How long your notification will continue to be retried for, unless the user acknowledges the notification.", - Placeholder: "1h", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "expire", - }, - { - Label: "TTL", - Description: "The number of seconds before a message expires and is deleted automatically. Examples: 10s, 5m30s, 8h.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "ttl", - }, - { - Label: "HTML", - Description: "Enables HTML formatting of the message.", - Element: ElementTypeCheckbox, - PropertyName: "html", - }, - v0HttpConfigOption(), - }, - } -} - -func getVictorOpsMimirNotifier() *NotifierPlugin { - return &NotifierPlugin{ - Type: "victorops", - Name: "VictorOps", - Description: "Send notifications to VictorOps", - Heading: "VictorOps settings", - Info: "", - Options: []NotifierOption{ - { - Label: "API key", - Description: "The API key to use when talking to the VictorOps API.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "api_key", - Secure: true, - }, - { - Label: "API URL", - Description: "The VictorOps API URL.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "api_url", - }, - { - Label: "Routing key", - Description: "A key used to map the alert to a team.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "routing_key", - Required: true, - }, - { - Label: "Message type", - Description: "Describes the behavior of the alert (CRITICAL, WARNING, INFO).", - Placeholder: promCfg.DefaultVictorOpsConfig.MessageType, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "message_type", - }, - { - Label: "Entity display name", - Description: "Contains summary of the alerted problem.", - Placeholder: promCfg.DefaultVictorOpsConfig.EntityDisplayName, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "entity_display_name", - }, - { - Label: "State message", - Description: "Contains long explanation of the alerted problem.", - Placeholder: promCfg.DefaultVictorOpsConfig.StateMessage, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "state_message", - }, - { - Label: "Monitoring tool", - Description: "The monitoring tool the state message is from.", - Placeholder: promCfg.DefaultVictorOpsConfig.MonitoringTool, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "monitoring_tool", - }, - { - Label: "Custom Fields", - Description: "A set of arbitrary key/value pairs that provide further detail about the alert.", - Element: ElementTypeKeyValueMap, - PropertyName: "custom_fields", - }, - v0HttpConfigOption(), - }, - } -} - -func getAmazonSNSMimirNotifier() *NotifierPlugin { - return &NotifierPlugin{ - Type: "sns", - Name: "Amazon SNS", - Description: "Sends notifications to Amazon SNS", - Heading: "Amazon SNS settings", - Info: "", - Options: []NotifierOption{ - { - Label: "API URL", - Description: "The Amazon SNS API URL", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "api_url", - }, - { - Label: "SigV4 authentication", - Description: "Configures AWS's Signature Verification 4 signing process to sign requests", - Element: ElementTypeSubform, - PropertyName: "sigv4", - SubformOptions: []NotifierOption{ - { - Label: "Region", - Description: "The AWS region. If blank, the region from the default credentials chain is used", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "Region", - }, - { - Label: "Access key", - Description: "The AWS API access_key. If blank the environment variable \"AWS_ACCESS_KEY_ID\" is used", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "AccessKey", - Secure: false, - }, - { - Label: "Secret key", - Description: "The AWS API secret_key. If blank the environment variable \"AWS_ACCESS_SECRET_ID\" is used", - Element: ElementTypeInput, - InputType: InputTypePassword, - PropertyName: "SecretKey", - Secure: true, - }, - { - Label: "Profile", - Description: "Named AWS profile used to authenticate", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "Profile", - }, - { - Label: "Role ARN", - Description: "AWS Role ARN, an alternative to using AWS API keys", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "RoleARN", - }, - }, - }, - { - Label: "SNS topic ARN", - Description: "If you don't specify this value, you must specify a value for the phone_number or target_arn. If you are using a FIFO SNS topic you should set a message group interval longer than 5 minutes to prevent messages with the same group key being deduplicated by the SNS default deduplication window", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "topic_arn", - }, - { - Label: "Phone number", - Description: "Phone number if message is delivered via SMS in E.164 format. If you don't specify this value, you must specify a value for the topic_arn or target_arn", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "phone_number", - }, - { - Label: "Target ARN", - Description: "The mobile platform endpoint ARN if message is delivered via mobile notifications. If you don't specify this value, you must specify a value for the topic_arn or phone_number", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "target_arn", - }, - { - Label: "Subject", - Description: "Subject line when the message is delivered", - Placeholder: promCfg.DefaultSNSConfig.Subject, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "subject", - }, - { - Label: "Message", - Description: "The message content of the SNS notification", - Placeholder: promCfg.DefaultSNSConfig.Message, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "message", - }, - { - Label: "Attributes", - Description: "SNS message attributes", - Element: ElementTypeKeyValueMap, - PropertyName: "attributes", - }, - v0HttpConfigOption(), - }, - } -} - -func getTelegramMimirNotifier() *NotifierPlugin { - return &NotifierPlugin{ - Type: "telegram", - Name: "Telegram", - Description: "Sends notifications to Telegram", - Heading: "Telegram settings", - Options: []NotifierOption{ - { - Label: "API URL", - Description: "The Telegram API URL", - Placeholder: "https://api.telegram.org", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "api_url", - }, - { - Label: "Bot token", - Description: "Telegram bot token", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "token", - Required: true, - Secure: true, - }, - { - Label: "Chat ID", - Description: "ID of the chat where to send the messages", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "chat_id", - Required: true, - }, - { - Label: "Message", - Description: "Message template", - Placeholder: promCfg.DefaultTelegramConfig.Message, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "message", - }, - { - Label: "Disable notifications", - Description: "Disable telegram notifications", - Element: ElementTypeCheckbox, - PropertyName: "disable_notifications", - }, - { - Label: "Parse mode", - Description: "Parse mode for telegram message", - Element: ElementTypeSelect, - PropertyName: "parse_mode", - SelectOptions: []SelectOption{ - {Value: "", Label: "None"}, - {Value: "MarkdownV2", Label: "MarkdownV2"}, - {Value: "Markdown", Label: "Markdown"}, - {Value: "HTML", Label: "HTML"}, - }, - }, - v0HttpConfigOption(), - }, - } -} - -func getWebexMimirNotifier() *NotifierPlugin { - return &NotifierPlugin{ - Type: "webex", - Name: "Cisco Webex Teams", - Description: "Sends notifications to Cisco Webex Teams", - Heading: "Cisco Webex Teams settings", - Info: "", - Options: []NotifierOption{ - { - Label: "API URL", - Description: "The Webex Teams API URL", - Placeholder: "https://webexapis.com/v1/messages", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "api_url", - }, - { - Label: "Room ID", - Description: "ID of the Webex Teams room where to send the messages", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "room_id", - Required: true, - }, - { - Label: "Message", - Description: "Message template", - Placeholder: promCfg.DefaultWebexConfig.Message, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "message", - }, - v0HttpConfigOption(), - }, - } -} - -func getMicrosoftTeamsMimirNotifier() *NotifierPlugin { - return &NotifierPlugin{ - Type: "teams", - TypeAlias: "msteams", - Name: "Microsoft Teams", - Description: "Sends notifications to Microsoft Teams", - Heading: "Microsoft Teams settings", - Options: []NotifierOption{ - { - Label: "Webhook URL", - Description: "The incoming webhook URL.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "webhook_url", - Secure: true, - Required: true, - }, - { - Label: "Title", - Description: "Message title template.", - Placeholder: promCfg.DefaultMSTeamsConfig.Title, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "title", - }, - { - Label: "Summary", - Description: "Message summary template.", - Placeholder: promCfg.DefaultMSTeamsConfig.Summary, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "summary", - }, - { - Label: "Text", - Description: "Message body template.", - Placeholder: promCfg.DefaultMSTeamsConfig.Text, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "text", - }, - v0HttpConfigOption(), - }, - } -} - -func getMicrosoftTeamsV2MimirNotifier() *NotifierPlugin { - return &NotifierPlugin{ - Type: "teams", - TypeAlias: "msteamsv2", - Name: "Microsoft Teams", - Description: "Sends notifications to Microsoft Teams using new format of adaptive cards", - Heading: "Microsoft Teams settings", - Options: []NotifierOption{ - { - Label: "Webhook URL", - Description: "The incoming webhook URL.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "webhook_url", - Secure: true, - Required: true, - }, - { - Label: "Title", - Description: "Message title template.", - Placeholder: promCfg.DefaultMSTeamsV2Config.Title, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "title", - }, - { - Label: "Text", - Description: "Message body template.", - Placeholder: promCfg.DefaultMSTeamsConfig.Text, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "text", - }, - v0HttpConfigOption(), - }, - } -} - -func getJiraMimirNotifier() *NotifierPlugin { - return &NotifierPlugin{ - Type: "jira", - Name: "Jira", - Description: "Send notifications to Jira Service Management", - Heading: "Jira settings", - Options: []NotifierOption{ - { - Label: "API URL", - Description: "The host to send Jira API requests to", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "api_url", - Required: true, - }, - { - Label: "Project Key", - Description: "The project key where issues are created", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "project", - Required: true, - }, - { - Label: "Issue Type", - Description: "Type of the issue (e.g. Bug)", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "issue_type", - Required: true, - }, - { - Label: "Summary", - Description: "Issue summary template", - Placeholder: promCfg.DefaultJiraConfig.Summary, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "summary", - }, - { - Label: "Description", - Description: "Issue description template", - Placeholder: promCfg.DefaultJiraConfig.Description, - Element: ElementTypeTextArea, - PropertyName: "description", - }, - { - Label: "Labels", - Description: " Labels to be added to the issue", - Element: ElementStringArray, - PropertyName: "labels", - }, - { - Label: "Priority", - Description: "Priority of the issue", - Placeholder: promCfg.DefaultJiraConfig.Priority, - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "priority", - }, - { - Label: "Reopen transition", - Description: "Name of the workflow transition to reopen an issue. The target status should not have the category \"done\"", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "reopen_transition", - }, - { - Label: "Resolve transition", - Description: "Name of the workflow transition to resolve an issue. The target status must have the category \"done\"", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "resolve_transition", - }, - { - Label: "Won't fix resolution", - Description: "If \"Reopen transition\" is defined, ignore issues with that resolution", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "wont_fix_resolution", - }, - { - Label: "Reopen duration", - Description: "If \"Reopen transition\" is defined, reopen the issue when it is not older than this value (rounded down to the nearest minute)", - Placeholder: "Use duration format, for example: 1.2s, 100ms", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "reopen_duration", - }, - { - Label: "Fields", - Description: "Other issue and custom fields", - Element: ElementTypeKeyValueMap, - PropertyName: "fields", - }, - v0HttpConfigOption(), - }, - } -} - -func v0ProxyConfigOptions() []NotifierOption { - return []NotifierOption{ - { - Label: "Proxy URL", - Description: "HTTP proxy server to use to connect to the targets.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "proxy_url", - }, - { - Label: "No Proxy", - Description: "Comma-separated list of domains for which the proxy should not be used.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "no_proxy", - }, - { - Label: "Proxy From Environment", - Description: "Makes use of net/http ProxyFromEnvironment function to determine proxies.", - Element: ElementTypeCheckbox, - PropertyName: "proxy_from_environment", - }, - { - Label: "Proxy Header Environment", - Description: "Headers to send to proxies during CONNECT requests.", - Element: ElementTypeKeyValueMap, - PropertyName: "proxy_connect_header", - }, - } -} - -func v0TLSConfigOption(propertyName string) NotifierOption { - return NotifierOption{ - Label: "TLS config", - Description: "Configures the TLS settings.", - PropertyName: propertyName, - Element: ElementTypeSubform, - SubformOptions: []NotifierOption{ - { - Label: "Server name", - Description: "ServerName extension to indicate the name of the server.", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "server_name", - }, - { - Label: "Skip verify", - Description: "Disable validation of the server certificate.", - Element: ElementTypeCheckbox, - PropertyName: "insecure_skip_verify", - }, - { - Label: "Min TLS Version", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "min_version", - }, - { - Label: "Max TLS Version", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "max_version", - }, - }, - } -} - -func v0HttpConfigOption() NotifierOption { - oauth2ConfigOption := func() NotifierOption { - return NotifierOption{ - Label: "OAuth2", - Description: "Configures the OAuth2 settings.", - PropertyName: "oauth2", - Element: ElementTypeSubform, - SubformOptions: []NotifierOption{ - { - Label: "Client ID", - Description: "The OAuth2 client ID", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "client_id", - Required: true, - }, - { - Label: "Client secret", - Description: "The OAuth2 client secret", - Element: ElementTypeInput, - InputType: InputTypePassword, - PropertyName: "client_secret", - Required: true, - Secure: true, - }, - { - Label: "Token URL", - Description: "The OAuth2 token exchange URL", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "token_url", - Required: true, - }, - { - Label: "Scopes", - Description: "Comma-separated list of scopes", - Element: ElementStringArray, - PropertyName: "scopes", - }, - { - Label: "Additional parameters", - Element: ElementTypeKeyValueMap, - PropertyName: "endpoint_params", - }, - v0TLSConfigOption("TLSConfig"), - }, - } - } - return NotifierOption{ - Label: "HTTP Config", - Description: "Note that `basic_auth` and `bearer_token` options are mutually exclusive.", - PropertyName: "http_config", - Element: ElementTypeSubform, - SubformOptions: append([]NotifierOption{ - { - Label: "Basic auth", - Description: "Sets the `Authorization` header with the configured username and password.", - PropertyName: "basic_auth", - Element: ElementTypeSubform, - SubformOptions: []NotifierOption{ - { - Label: "Username", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "username", - }, - { - Label: "Password", - Element: ElementTypeInput, - InputType: InputTypePassword, - PropertyName: "password", - Secure: true, - }, - }, - }, - { - Label: "Authorization", - Description: "The HTTP authorization credentials for the targets.", - Element: ElementTypeSubform, - PropertyName: "authorization", - SubformOptions: []NotifierOption{ - { - Label: "Type", - Element: ElementTypeInput, - InputType: InputTypeText, - PropertyName: "type", - }, - { - Label: "Credentials", - Element: ElementTypeInput, - InputType: InputTypePassword, - PropertyName: "credentials", - Secure: true, - }, - }, - }, - { - Label: "Follow redirects", - Description: "Whether the client should follow HTTP 3xx redirects.", - Element: ElementTypeCheckbox, - PropertyName: "follow_redirects", - }, - { - Label: "Enable HTTP2", - Description: "Whether the client should configure HTTP2.", - Element: ElementTypeCheckbox, - PropertyName: "enable_http2", - }, - { - Label: "HTTP Headers", - Description: "Headers to inject in the requests.", - Element: ElementTypeKeyValueMap, - PropertyName: "http_headers", - }, - }, append( - v0ProxyConfigOptions(), - v0TLSConfigOption("tls_config"), - oauth2ConfigOption())..., - ), - } -} diff --git a/pkg/services/ngalert/notifier/channels_config/available_channels_test.go b/pkg/services/ngalert/notifier/channels_config/available_channels_test.go deleted file mode 100644 index 0d3532b99a3..00000000000 --- a/pkg/services/ngalert/notifier/channels_config/available_channels_test.go +++ /dev/null @@ -1,290 +0,0 @@ -package channels_config - -import ( - "encoding/json" - "fmt" - "maps" - "reflect" - "slices" - "strings" - "testing" - - "github.com/grafana/alerting/notify/notifytest" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestGetSecretKeysForContactPointType(t *testing.T) { - httpConfigSecrets := []string{"http_config.authorization.credentials", "http_config.basic_auth.password", "http_config.oauth2.client_secret"} - testCases := []struct { - receiverType string - version NotifierVersion - expectedSecretFields []string - }{ - {receiverType: "dingding", version: V1, expectedSecretFields: []string{"url"}}, - {receiverType: "kafka", version: V1, expectedSecretFields: []string{"password"}}, - {receiverType: "email", version: V1, expectedSecretFields: []string{}}, - {receiverType: "pagerduty", version: V1, expectedSecretFields: []string{"integrationKey"}}, - {receiverType: "victorops", version: V1, expectedSecretFields: []string{"url"}}, - {receiverType: "oncall", version: V1, expectedSecretFields: []string{"password", "authorization_credentials"}}, - {receiverType: "pushover", version: V1, expectedSecretFields: []string{"apiToken", "userKey"}}, - {receiverType: "slack", version: V1, expectedSecretFields: []string{"token", "url"}}, - {receiverType: "sensugo", version: V1, expectedSecretFields: []string{"apikey"}}, - {receiverType: "teams", version: V1, expectedSecretFields: []string{}}, - {receiverType: "telegram", version: V1, expectedSecretFields: []string{"bottoken"}}, - {receiverType: "webhook", version: V1, expectedSecretFields: []string{ - "password", - "authorization_credentials", - "tlsConfig.caCertificate", - "tlsConfig.clientCertificate", - "tlsConfig.clientKey", - "hmacConfig.secret", - "http_config.oauth2.client_secret", - "http_config.oauth2.tls_config.caCertificate", - "http_config.oauth2.tls_config.clientCertificate", - "http_config.oauth2.tls_config.clientKey", - }}, - {receiverType: "wecom", version: V1, expectedSecretFields: []string{"url", "secret"}}, - {receiverType: "prometheus-alertmanager", version: V1, expectedSecretFields: []string{"basicAuthPassword"}}, - {receiverType: "discord", version: V1, expectedSecretFields: []string{"url"}}, - {receiverType: "googlechat", version: V1, expectedSecretFields: []string{"url"}}, - {receiverType: "LINE", version: V1, expectedSecretFields: []string{"token"}}, - {receiverType: "threema", version: V1, expectedSecretFields: []string{"api_secret"}}, - {receiverType: "opsgenie", version: V1, expectedSecretFields: []string{"apiKey"}}, - {receiverType: "webex", version: V1, expectedSecretFields: []string{"bot_token"}}, - {receiverType: "sns", version: V1, expectedSecretFields: []string{"sigv4.access_key", "sigv4.secret_key"}}, - {receiverType: "mqtt", version: V1, expectedSecretFields: []string{"password", "tlsConfig.caCertificate", "tlsConfig.clientCertificate", "tlsConfig.clientKey"}}, - {receiverType: "jira", version: V1, expectedSecretFields: []string{"user", "password", "api_token"}}, - {receiverType: "victorops", version: V0mimir1, expectedSecretFields: append([]string{"api_key"}, httpConfigSecrets...)}, - {receiverType: "sns", version: V0mimir1, expectedSecretFields: append([]string{"sigv4.SecretKey"}, httpConfigSecrets...)}, - {receiverType: "telegram", version: V0mimir1, expectedSecretFields: append([]string{"token"}, httpConfigSecrets...)}, - {receiverType: "discord", version: V0mimir1, expectedSecretFields: append([]string{"webhook_url"}, httpConfigSecrets...)}, - {receiverType: "pagerduty", version: V0mimir1, expectedSecretFields: append([]string{"routing_key", "service_key"}, httpConfigSecrets...)}, - {receiverType: "pushover", version: V0mimir1, expectedSecretFields: append([]string{"user_key", "token"}, httpConfigSecrets...)}, - {receiverType: "jira", version: V0mimir1, expectedSecretFields: httpConfigSecrets}, - {receiverType: "opsgenie", version: V0mimir1, expectedSecretFields: append([]string{"api_key"}, httpConfigSecrets...)}, - {receiverType: "teams", version: V0mimir1, expectedSecretFields: append([]string{"webhook_url"}, httpConfigSecrets...)}, - {receiverType: "teams", version: V0mimir2, expectedSecretFields: append([]string{"webhook_url"}, httpConfigSecrets...)}, - {receiverType: "email", version: V0mimir1, expectedSecretFields: []string{"auth_password", "auth_secret"}}, - {receiverType: "slack", version: V0mimir1, expectedSecretFields: append([]string{"api_url"}, httpConfigSecrets...)}, - {receiverType: "webex", version: V0mimir1, expectedSecretFields: httpConfigSecrets}, - {receiverType: "wechat", version: V0mimir1, expectedSecretFields: append([]string{"api_secret"}, httpConfigSecrets...)}, - {receiverType: "webhook", version: V0mimir1, expectedSecretFields: append([]string{"url"}, httpConfigSecrets...)}, - } - n := slices.Collect(GetAvailableNotifiersV2()) - type typeWithVersion struct { - Type string - Version NotifierVersion - } - allTypes := make(map[typeWithVersion]struct{}, len(n)) - getKey := func(pluginType string, version NotifierVersion) typeWithVersion { - return typeWithVersion{pluginType, version} - } - for _, p := range n { - for _, v := range p.Versions { - allTypes[getKey(p.Type, v.Version)] = struct{}{} - } - } - - for _, testCase := range testCases { - delete(allTypes, getKey(testCase.receiverType, testCase.version)) - t.Run(fmt.Sprintf("%s-%s", testCase.receiverType, testCase.version), func(t *testing.T) { - got, err := GetSecretKeysForContactPointType(testCase.receiverType, testCase.version) - require.NoError(t, err) - require.ElementsMatch(t, testCase.expectedSecretFields, got) - }) - } - - for it := range allTypes { - t.Run(fmt.Sprintf("%s-%s", it.Type, it.Version), func(t *testing.T) { - got, err := GetSecretKeysForContactPointType(it.Type, it.Version) - require.NoError(t, err) - require.Emptyf(t, got, "secret keys for version %s of %s should be empty", it.Version, it.Type) - }) - } - - require.Emptyf(t, allTypes, "not all types are covered: %s", allTypes) -} - -func TestGetAvailableNotifiersV2(t *testing.T) { - n := slices.Collect(GetAvailableNotifiersV2()) - require.NotEmpty(t, n) - for _, notifier := range n { - t.Run(fmt.Sprintf("integration %s [%s]", notifier.Type, notifier.Name), func(t *testing.T) { - currentVersion := V1 - if notifier.Type == "wechat" { - currentVersion = V0mimir1 - } - t.Run(fmt.Sprintf("current version is %s", currentVersion), func(t *testing.T) { - require.Equal(t, currentVersion, notifier.GetCurrentVersion().Version) - }) - t.Run("should be able to create only v1", func(t *testing.T) { - for _, version := range notifier.Versions { - if version.Version == V1 { - require.True(t, version.CanCreate, "v1 should be able to create") - continue - } - require.False(t, version.CanCreate, "v0 should not be able to create") - } - }) - }) - } -} - -func TestConfigForIntegrationType(t *testing.T) { - t.Run("should return current version for all common types", func(t *testing.T) { - for plugin := range GetAvailableNotifiersV2() { - t.Run(plugin.Type, func(t *testing.T) { - version, err := ConfigForIntegrationType(plugin.Type) - require.NoErrorf(t, err, "expected config but got error for plugin type %s", plugin.Type) - assert.Equal(t, version.Plugin, plugin) - assert.Equal(t, version, plugin.GetCurrentVersion()) - }) - } - }) - - t.Run("should return specific version if matched by alias", func(t *testing.T) { - for plugin := range GetAvailableNotifiersV2() { - for _, version := range plugin.Versions { - if version.TypeAlias == "" { - continue - } - t.Run(version.TypeAlias, func(t *testing.T) { - actualVersion, err := ConfigForIntegrationType(version.TypeAlias) - require.NoErrorf(t, err, "expected config but got error for plugin type %s", plugin.Type) - assert.Equal(t, version, actualVersion) - }) - } - } - }) - - t.Run("should return error if not known type", func(t *testing.T) { - _, err := ConfigForIntegrationType("unknown") - require.Error(t, err) - }) -} - -func TestTypeUniqueness(t *testing.T) { - knownTypes := make(map[string]struct{}) - for plugin := range GetAvailableNotifiersV2() { - iType := strings.ToLower(plugin.Type) - if _, ok := knownTypes[iType]; ok { - assert.Failf(t, "duplicate plugin type", "plugin type %s", plugin.Type) - } - knownTypes[iType] = struct{}{} - for _, version := range plugin.Versions { - if version.TypeAlias == "" { - continue - } - iType = strings.ToLower(version.TypeAlias) - if _, ok := knownTypes[iType]; ok { - assert.Failf(t, "mimir type duplicates Grafana plugin type", "plugin type %s", iType) - } - knownTypes[iType] = struct{}{} - } - } -} - -func Test_getSecretFields(t *testing.T) { - testCases := []struct { - name string - parentPath string - options []NotifierOption - expectedFields []string - }{ - { - name: "No secure fields", - parentPath: "", - options: []NotifierOption{ - {PropertyName: "field1", Secure: false, SubformOptions: nil}, - {PropertyName: "field2", Secure: false, SubformOptions: nil}, - }, - expectedFields: []string{}, - }, - { - name: "Single secure field", - parentPath: "", - options: []NotifierOption{ - {PropertyName: "field1", Secure: true, SubformOptions: nil}, - {PropertyName: "field2", Secure: false, SubformOptions: nil}, - }, - expectedFields: []string{"field1"}, - }, - { - name: "Secure field in subform", - parentPath: "parent", - options: []NotifierOption{ - {PropertyName: "field1", Secure: true, SubformOptions: nil}, - {PropertyName: "field2", Secure: false, SubformOptions: []NotifierOption{ - {PropertyName: "subfield1", Secure: true, SubformOptions: nil}, - }}, - }, - expectedFields: []string{"parent.field1", "parent.field2.subfield1"}, - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - got := getSecretFields(tc.parentPath, tc.options) - require.ElementsMatch(t, got, tc.expectedFields) - }) - } -} - -func TestV0IntegrationsSecrets(t *testing.T) { - // This test ensures that all known integrations' secrets are listed in the schema definition. - notifytest.ForEachIntegrationType(t, func(configType reflect.Type) { - t.Run(configType.Name(), func(t *testing.T) { - integrationType := strings.ToLower(strings.TrimSuffix(configType.Name(), "Config")) - pluginVersion, err := ConfigForIntegrationType(integrationType) - require.NoError(t, err) - if pluginVersion.Version == V1 { - var ok bool - pluginVersion, ok = pluginVersion.Plugin.GetVersion(V0mimir1) - require.True(t, ok) - } - expectedSecrets := pluginVersion.GetSecretFieldsPaths() - var secrets []string - for option := range maps.Keys(notifytest.ValidMimirHTTPConfigs) { - cfg, err := notifytest.GetMimirIntegrationForType(configType, option) - require.NoError(t, err) - data, err := json.Marshal(cfg) - require.NoError(t, err) - m := map[string]any{} - err = json.Unmarshal(data, &m) - require.NoError(t, err) - secrets = append(secrets, getSecrets(m, "")...) - } - secrets = unique(secrets) - t.Log(secrets) - require.ElementsMatch(t, expectedSecrets, secrets) - }) - }) -} - -func unique(slice []string) []string { - keys := make(map[string]struct{}, len(slice)) - list := make([]string, 0, len(slice)) - for _, entry := range slice { - if _, value := keys[entry]; !value { - keys[entry] = struct{}{} - list = append(list, entry) - } - } - return list -} - -func getSecrets(m map[string]any, parent string) []string { - var result []string - for key, val := range m { - str, ok := val.(string) - if ok && str == "" { - result = append(result, parent+key) - } - m, ok := val.(map[string]any) - if ok { - subSecrets := getSecrets(m, parent+key+".") - result = append(result, subSecrets...) - } - } - return result -} diff --git a/pkg/services/ngalert/notifier/channels_config/plugin.go b/pkg/services/ngalert/notifier/channels_config/plugin.go deleted file mode 100644 index a38244989d5..00000000000 --- a/pkg/services/ngalert/notifier/channels_config/plugin.go +++ /dev/null @@ -1,122 +0,0 @@ -package channels_config - -// NotifierPlugin holds meta information about a notifier. -type NotifierPlugin struct { - Type string `json:"type"` - TypeAlias string `json:"typeAlias,omitempty"` - Name string `json:"name"` - Heading string `json:"heading"` - Description string `json:"description"` - Info string `json:"info"` - Options []NotifierOption `json:"options"` -} - -// VersionedNotifierPlugin represents a notifier plugin with multiple versions and detailed configuration options. -// It includes metadata such as type, name, description, and version-specific details. -type VersionedNotifierPlugin struct { - Type string `json:"type"` - CurrentVersion NotifierVersion `json:"currentVersion"` - Name string `json:"name"` - Heading string `json:"heading,omitempty"` - Description string `json:"description,omitempty"` - Info string `json:"info,omitempty"` - Versions []NotifierPluginVersion `json:"versions"` -} - -// GetVersion retrieves a specific version of the notifier plugin by its version string. Returns the version and a boolean indicating success. -func (p VersionedNotifierPlugin) GetVersion(v NotifierVersion) (NotifierPluginVersion, bool) { - for _, version := range p.Versions { - if version.Version == v { - return version, true - } - } - return NotifierPluginVersion{}, false -} - -// GetCurrentVersion retrieves the current version of the notifier plugin based on the CurrentVersion property. -// Panics if the version specified in CurrentVersion is not found in the configured versions. -func (p VersionedNotifierPlugin) GetCurrentVersion() NotifierPluginVersion { - v, ok := p.GetVersion(p.CurrentVersion) - if !ok { - panic("version not found for current version: " + p.CurrentVersion) - } - return v -} - -// NotifierPluginVersion represents a version of a notifier plugin, including configuration options and metadata. -type NotifierPluginVersion struct { - TypeAlias string `json:"typeAlias,omitempty"` - Version NotifierVersion `json:"version"` - CanCreate bool `json:"canCreate"` - Options []NotifierOption `json:"options"` - Info string `json:"info,omitempty"` - Plugin *VersionedNotifierPlugin `json:"-"` -} - -// GetSecretFieldsPaths returns a list of paths for fields marked as secure within the NotifierPluginVersion's options. -func (v NotifierPluginVersion) GetSecretFieldsPaths() []string { - return getSecretFields("", v.Options) -} - -// NotifierOption holds information about options specific for the NotifierPlugin. -type NotifierOption struct { - Element ElementType `json:"element"` - InputType InputType `json:"inputType"` - Label string `json:"label"` - Description string `json:"description"` - Placeholder string `json:"placeholder"` - PropertyName string `json:"propertyName"` - SelectOptions []SelectOption `json:"selectOptions"` - ShowWhen ShowWhen `json:"showWhen"` - Required bool `json:"required"` - ValidationRule string `json:"validationRule"` - Secure bool `json:"secure"` - DependsOn string `json:"dependsOn"` - SubformOptions []NotifierOption `json:"subformOptions"` -} - -// ElementType is the type of element that can be rendered in the frontend. -type ElementType string - -const ( - // ElementTypeInput will render an input - ElementTypeInput = "input" - // ElementTypeSelect will render a select - ElementTypeSelect = "select" - // ElementTypeCheckbox will render a checkbox - ElementTypeCheckbox = "checkbox" - // ElementTypeTextArea will render a textarea - ElementTypeTextArea = "textarea" - // ElementTypeKeyValueMap will render inputs to add arbitrary key-value pairs - ElementTypeKeyValueMap = "key_value_map" - // ElementSubformArray will render a sub-form with schema defined in SubformOptions - ElementTypeSubform = "subform" - // ElementSubformArray will render a multiple sub-forms with schema defined in SubformOptions - ElementSubformArray = "subform_array" - // ElementStringArray will render a set of fields to manage an array of strings. - ElementStringArray = "string_array" -) - -// InputType is the type of input that can be rendered in the frontend. -type InputType string - -const ( - // InputTypeText will render a text field in the frontend - InputTypeText = "text" - // InputTypePassword will render a password field in the frontend - InputTypePassword = "password" -) - -// SelectOption is a simple type for Options that have dropdown options. Should be used when Element is ElementTypeSelect. -type SelectOption struct { - Value string `json:"value"` - Label string `json:"label"` -} - -// ShowWhen holds information about when options are dependant on other options. -// Should be used when Element is ElementTypeSelect. -// Does not work for ElementTypeCheckbox. -type ShowWhen struct { - Field string `json:"field"` - Is string `json:"is"` -} diff --git a/pkg/services/ngalert/notifier/crypto.go b/pkg/services/ngalert/notifier/crypto.go index c147fa4cec2..51aa05cbc8b 100644 --- a/pkg/services/ngalert/notifier/crypto.go +++ b/pkg/services/ngalert/notifier/crypto.go @@ -8,10 +8,12 @@ import ( "fmt" "strings" + alertingNotify "github.com/grafana/alerting/notify" + "github.com/grafana/alerting/receivers/schema" + "github.com/grafana/grafana/pkg/infra/log" "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/notifier/channels_config" "github.com/grafana/grafana/pkg/services/ngalert/notifier/legacy_storage" "github.com/grafana/grafana/pkg/services/ngalert/store" "github.com/grafana/grafana/pkg/services/secrets" @@ -107,11 +109,11 @@ func encryptReceiverConfigs(c []*definitions.PostableApiReceiver, encrypt defini return fmt.Errorf("integration '%s' of receiver '%s' has settings that cannot be parsed as JSON: %w", gr.Type, gr.Name, err) } - secretKeys, err := channels_config.GetSecretKeysForContactPointType(gr.Type, channels_config.V1) - if err != nil { - return fmt.Errorf("failed to get secret keys for contact point type %s: %w", gr.Type, err) + typeSchema, ok := alertingNotify.GetSchemaVersionForIntegration(schema.IntegrationType(gr.Type), schema.V1) + if !ok { + return fmt.Errorf("failed to get secret keys for contact point type %s", gr.Type) } - + secretKeys := typeSchema.GetSecretFieldsPaths() secureSettings := gr.SecureSettings if secureSettings == nil { secureSettings = make(map[string]string) diff --git a/pkg/services/ngalert/notifier/email_test.go b/pkg/services/ngalert/notifier/email_test.go index 32606a95c7f..87c73b183f4 100644 --- a/pkg/services/ngalert/notifier/email_test.go +++ b/pkg/services/ngalert/notifier/email_test.go @@ -7,7 +7,7 @@ import ( alertingImages "github.com/grafana/alerting/images" "github.com/grafana/alerting/receivers" - alertingEmail "github.com/grafana/alerting/receivers/email" + alertingEmail "github.com/grafana/alerting/receivers/email/v1" alertingTemplates "github.com/grafana/alerting/templates" "github.com/prometheus/alertmanager/types" "github.com/prometheus/common/model" diff --git a/pkg/services/ngalert/notifier/legacy_storage/receivers_test.go b/pkg/services/ngalert/notifier/legacy_storage/receivers_test.go index 4bf74aa7866..72ce37763aa 100644 --- a/pkg/services/ngalert/notifier/legacy_storage/receivers_test.go +++ b/pkg/services/ngalert/notifier/legacy_storage/receivers_test.go @@ -8,13 +8,14 @@ import ( "github.com/grafana/alerting/definition" "github.com/grafana/alerting/notify" + "github.com/grafana/alerting/receivers/schema" + "github.com/grafana/alerting/receivers/webhook" "github.com/prometheus/alertmanager/config" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" "github.com/grafana/grafana/pkg/services/ngalert/models" - "github.com/grafana/grafana/pkg/util" ) func TestReceiverInUse(t *testing.T) { @@ -91,8 +92,9 @@ func TestDeleteReceiver(t *testing.T) { } func TestCreateReceiver(t *testing.T) { - rawCfg := notify.AllKnownConfigsForTesting["webhook"] - cfgSchema, err := models.IntegrationConfigFromType(rawCfg.NotifierType, util.Pointer("v1")) + rawCfg := notify.AllKnownConfigsForTesting[string(webhook.Type)] + typeSchema, _ := notify.GetSchemaForIntegration(webhook.Type) + cfgSchema, err := models.IntegrationConfigFromSchema(typeSchema, schema.V1) require.NoError(t, err) settings := map[string]any{} require.NoError(t, json.Unmarshal([]byte(rawCfg.Config), &settings)) @@ -197,8 +199,9 @@ func TestCreateReceiver(t *testing.T) { } func TestUpdateReceiver(t *testing.T) { - rawCfg := notify.AllKnownConfigsForTesting["webhook"] - cfgSchema, err := models.IntegrationConfigFromType(rawCfg.NotifierType, util.Pointer("v1")) + rawCfg := notify.AllKnownConfigsForTesting[string(webhook.Type)] + typeSchema, _ := notify.GetSchemaForIntegration(webhook.Type) + cfgSchema, err := models.IntegrationConfigFromSchema(typeSchema, schema.V1) require.NoError(t, err) settings := map[string]any{} require.NoError(t, json.Unmarshal([]byte(rawCfg.Config), &settings)) @@ -297,8 +300,9 @@ func TestUpdateReceiver(t *testing.T) { } func TestGetReceiver(t *testing.T) { - rawCfg := notify.AllKnownConfigsForTesting["webhook"] - cfgSchema, err := models.IntegrationConfigFromType(rawCfg.NotifierType, util.Pointer("v1")) + rawCfg := notify.AllKnownConfigsForTesting[string(webhook.Type)] + typeSchema, _ := notify.GetSchemaForIntegration(webhook.Type) + cfgSchema, err := models.IntegrationConfigFromSchema(typeSchema, schema.V1) require.NoError(t, err) settings := map[string]any{} require.NoError(t, json.Unmarshal([]byte(rawCfg.Config), &settings)) diff --git a/pkg/services/ngalert/provisioning/contactpoints.go b/pkg/services/ngalert/provisioning/contactpoints.go index c7ceadad3c8..d349acfa92c 100644 --- a/pkg/services/ngalert/provisioning/contactpoints.go +++ b/pkg/services/ngalert/provisioning/contactpoints.go @@ -9,6 +9,7 @@ import ( "strings" alertingNotify "github.com/grafana/alerting/notify" + "github.com/grafana/alerting/receivers/schema" "github.com/prometheus/alertmanager/config" "github.com/grafana/grafana/pkg/apimachinery/identity" @@ -17,7 +18,6 @@ import ( ac "github.com/grafana/grafana/pkg/services/accesscontrol" apimodels "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/notifier/channels_config" "github.com/grafana/grafana/pkg/services/ngalert/notifier/legacy_storage" "github.com/grafana/grafana/pkg/services/ngalert/store" "github.com/grafana/grafana/pkg/services/secrets" @@ -247,11 +247,11 @@ func (ecp *ContactPointService) UpdateContactPoint(ctx context.Context, orgID in if err != nil { return err } - secretKeys, err := channels_config.GetSecretKeysForContactPointType(contactPoint.Type, channels_config.V1) - if err != nil { - return fmt.Errorf("%w: %s", ErrValidation, err.Error()) + typeSchema, ok := alertingNotify.GetSchemaVersionForIntegration(schema.IntegrationType(contactPoint.Type), schema.V1) + if !ok { + return fmt.Errorf("%w: failed to get secret keys for contact point type %s", ErrValidation, contactPoint.Type) } - for _, secretKey := range secretKeys { + for _, secretKey := range typeSchema.GetSecretFieldsPaths() { secretValue := contactPoint.Settings.Get(secretKey).MustString() if secretValue == apimodels.RedactedValue { contactPoint.Settings.Set(secretKey, rawContactPoint.Settings.Get(secretKey).MustString()) @@ -522,11 +522,11 @@ func ValidateContactPoint(ctx context.Context, e apimodels.EmbeddedContactPoint, // RemoveSecretsForContactPoint removes all secrets from the contact point's settings and returns them as a map. Returns error if contact point type is not known. func RemoveSecretsForContactPoint(e *apimodels.EmbeddedContactPoint) (map[string]string, error) { s := map[string]string{} - secretKeys, err := channels_config.GetSecretKeysForContactPointType(e.Type, channels_config.V1) - if err != nil { - return nil, err + typeSchema, ok := alertingNotify.GetSchemaVersionForIntegration(schema.IntegrationType(e.Type), schema.V1) + if !ok { + return nil, fmt.Errorf("failed to get secret keys for contact point type %s", e.Type) } - for _, secretKey := range secretKeys { + for _, secretKey := range typeSchema.GetSecretFieldsPaths() { secretValue, err := extractCaseInsensitive(e.Settings, secretKey) if err != nil { return nil, err diff --git a/pkg/services/ngalert/provisioning/contactpoints_test.go b/pkg/services/ngalert/provisioning/contactpoints_test.go index 120c90fe4ba..b85dfcd3108 100644 --- a/pkg/services/ngalert/provisioning/contactpoints_test.go +++ b/pkg/services/ngalert/provisioning/contactpoints_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/grafana/alerting/notify" + "github.com/grafana/alerting/receivers/schema" "github.com/prometheus/alertmanager/config" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -25,7 +26,6 @@ import ( "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/notifier" - "github.com/grafana/grafana/pkg/services/ngalert/notifier/channels_config" "github.com/grafana/grafana/pkg/services/ngalert/notifier/legacy_storage" "github.com/grafana/grafana/pkg/services/ngalert/tests/fakes" "github.com/grafana/grafana/pkg/services/secrets" @@ -441,15 +441,14 @@ func TestRemoveSecretsForContactPoint(t *testing.T) { keys := maps.Keys(configs) slices.Sort(keys) for _, integrationType := range keys { - integration := models.IntegrationGen(models.IntegrationMuts.WithValidConfig(integrationType))() + integration := models.IntegrationGen(models.IntegrationMuts.WithValidConfig(schema.IntegrationType(integrationType)))() if f, ok := overrides[integrationType]; ok { f(integration.Settings) } settingsRaw, err := json.Marshal(integration.Settings) require.NoError(t, err) - - expectedFields, err := channels_config.GetSecretKeysForContactPointType(integrationType, channels_config.V1) - require.NoError(t, err) + typeSchema, _ := notify.GetSchemaVersionForIntegration(schema.IntegrationType(integrationType), schema.V1) + expectedFields := typeSchema.GetSecretFieldsPaths() t.Run(integrationType, func(t *testing.T) { cp := definitions.EmbeddedContactPoint{ diff --git a/pkg/tests/api/alerting/api_available_channel_test.go b/pkg/tests/api/alerting/api_available_channel_test.go index b0b3751299e..561e0dd501e 100644 --- a/pkg/tests/api/alerting/api_available_channel_test.go +++ b/pkg/tests/api/alerting/api_available_channel_test.go @@ -13,7 +13,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/grafana/grafana/pkg/services/ngalert/notifier/channels_config" "github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/tests/testinfra" @@ -54,10 +53,17 @@ func TestIntegrationAvailableChannels(t *testing.T) { require.NoError(t, err) require.Equal(t, 200, resp.StatusCode) - expNotifiers := channels_config.GetAvailableNotifiers() - expJson, err := json.Marshal(expNotifiers) + expectedBytes, err := os.ReadFile(path.Join("test-data", "alert-notifiers-v1-snapshot.json")) require.NoError(t, err) - require.Equal(t, string(expJson), string(b)) + + require.NoError(t, err) + if !assert.JSONEq(t, string(expectedBytes), string(b)) { + var prettyJSON bytes.Buffer + err := json.Indent(&prettyJSON, b, "", " ") + require.NoError(t, err) + err = os.WriteFile(path.Join("test-data", "alert-notifiers-v1-snapshot.json"), prettyJSON.Bytes(), 0o644) + require.NoError(t, err) + } }) t.Run("should return versioned notifiers", func(t *testing.T) { diff --git a/pkg/tests/api/alerting/api_notification_channel_test.go b/pkg/tests/api/alerting/api_notification_channel_test.go index 6d486055279..52c41586f31 100644 --- a/pkg/tests/api/alerting/api_notification_channel_test.go +++ b/pkg/tests/api/alerting/api_notification_channel_test.go @@ -17,11 +17,11 @@ import ( "time" "github.com/grafana/alerting/receivers" - alertingLine "github.com/grafana/alerting/receivers/line" - alertingPushover "github.com/grafana/alerting/receivers/pushover" - alertingSlack "github.com/grafana/alerting/receivers/slack" - alertingTelegram "github.com/grafana/alerting/receivers/telegram" - alertingThreema "github.com/grafana/alerting/receivers/threema" + alertingLine "github.com/grafana/alerting/receivers/line/v1" + alertingPushover "github.com/grafana/alerting/receivers/pushover/v1" + alertingSlack "github.com/grafana/alerting/receivers/slack/v1" + alertingTelegram "github.com/grafana/alerting/receivers/telegram/v1" + alertingThreema "github.com/grafana/alerting/receivers/threema/v1" alertingTemplates "github.com/grafana/alerting/templates" "github.com/prometheus/alertmanager/template" "github.com/prometheus/common/model" diff --git a/pkg/tests/api/alerting/test-data/alert-notifiers-v1-snapshot.json b/pkg/tests/api/alerting/test-data/alert-notifiers-v1-snapshot.json new file mode 100644 index 00000000000..59324e97edd --- /dev/null +++ b/pkg/tests/api/alerting/test-data/alert-notifiers-v1-snapshot.json @@ -0,0 +1,4310 @@ +[ + { + "type": "LINE", + "name": "LINE", + "heading": "LINE notify settings", + "description": "Send notifications to LINE notify", + "info": "", + "options": [ + { + "element": "input", + "inputType": "text", + "label": "Token", + "description": "", + "placeholder": "LINE notify token key", + "propertyName": "token", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "text", + "label": "Title", + "description": "Templated title of the message", + "placeholder": "{{ template \"default.title\" . }}", + "propertyName": "title", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "text", + "label": "Description", + "description": "Templated description of the message", + "placeholder": "{{ template \"default.message\" . }}", + "propertyName": "description", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + } + ] + }, + { + "type": "dingding", + "name": "DingDing", + "heading": "DingDing settings", + "description": "Sends HTTP POST request to DingDing", + "info": "", + "options": [ + { + "element": "input", + "inputType": "text", + "label": "URL", + "description": "", + "placeholder": "https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxx", + "propertyName": "url", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "select", + "inputType": "", + "label": "Message Type", + "description": "", + "placeholder": "", + "propertyName": "msgType", + "selectOptions": [ + { + "value": "link", + "label": "Link" + }, + { + "value": "actionCard", + "label": "ActionCard" + } + ], + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Title", + "description": "Templated title of the message", + "placeholder": "{{ template \"default.title\" . }}", + "propertyName": "title", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "", + "label": "Message", + "description": "Custom DingDing message. You can use template variables.", + "placeholder": "{{ template \"default.message\" . }}", + "propertyName": "message", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + } + ] + }, + { + "type": "discord", + "name": "Discord", + "heading": "Discord settings", + "description": "Sends notifications to Discord", + "info": "", + "options": [ + { + "element": "textarea", + "inputType": "text", + "label": "Title", + "description": "Templated title of the message", + "placeholder": "{{ template \"default.title\" . }}", + "propertyName": "title", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "text", + "label": "Message Content", + "description": "Mention a group using @ or a user using \u003c@ID\u003e when notifying in a channel", + "placeholder": "{{ template \"default.message\" . }}", + "propertyName": "message", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Webhook URL", + "description": "", + "placeholder": "Discord webhook URL", + "propertyName": "url", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Avatar URL", + "description": "", + "placeholder": "", + "propertyName": "avatar_url", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "checkbox", + "inputType": "", + "label": "Use Discord's Webhook Username", + "description": "Use the username configured in Discord's webhook settings. Otherwise, the username will be 'Grafana'", + "placeholder": "", + "propertyName": "use_discord_username", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + } + ] + }, + { + "type": "email", + "name": "Email", + "heading": "Email settings", + "description": "Sends notifications using Grafana server configured SMTP settings", + "info": "", + "options": [ + { + "element": "checkbox", + "inputType": "", + "label": "Single email", + "description": "Send a single email to all recipients", + "placeholder": "", + "propertyName": "singleEmail", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "", + "label": "Addresses", + "description": "You can enter multiple email addresses using a \";\", \"\\n\" or \",\" separator", + "placeholder": "", + "propertyName": "addresses", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "", + "label": "Message", + "description": "Optional message. You can use templates to customize this field. Using a custom message will replace the default message", + "placeholder": "{{ template \"default.message\" . }}", + "propertyName": "message", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "text", + "label": "Subject", + "description": "Optional subject. You can use templates to customize this field", + "placeholder": "{{ template \"default.title\" . }}", + "propertyName": "subject", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + } + ] + }, + { + "type": "googlechat", + "name": "Google Chat", + "heading": "Google Chat settings", + "description": "Sends notifications to Google Chat via webhooks based on the official JSON message format", + "info": "", + "options": [ + { + "element": "input", + "inputType": "text", + "label": "URL", + "description": "", + "placeholder": "Google Chat incoming webhook url", + "propertyName": "url", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "text", + "label": "Title", + "description": "Templated title of the message", + "placeholder": "{{ template \"default.title\" . }}", + "propertyName": "title", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "", + "label": "Message", + "description": "", + "placeholder": "{{ template \"default.message\" . }}", + "propertyName": "message", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + } + ] + }, + { + "type": "jira", + "name": "Jira", + "heading": "Jira settings", + "description": "Creates Jira issues from alerts", + "info": "", + "options": [ + { + "element": "input", + "inputType": "text", + "label": "API URL of Jira instance, including version of API", + "description": "Supported v2 or v3 APIs", + "placeholder": "https://grafana.atlassian.net/rest/api/3", + "propertyName": "api_url", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "HTTP Basic Authentication - Username", + "description": "Username to use for Jira authentication.", + "placeholder": "", + "propertyName": "user", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "password", + "label": "HTTP Basic Authentication - Password", + "description": "Password to use for Jira authentication.", + "placeholder": "", + "propertyName": "password", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "password", + "label": "Authorization Header - Personal Access Token", + "description": "Personal Access Token that is used as a bearer authorization header.", + "placeholder": "", + "propertyName": "api_token", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Project Key", + "description": "The project key associated with the relevant Jira project", + "placeholder": "Grafana", + "propertyName": "project", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Issue Type", + "description": "The type of the Jira issue (e.g., Bug, Task, Story). You can use templates to customize this field.", + "placeholder": "Task", + "propertyName": "issue_type", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Summary", + "description": "The summary of the Jira issue. You can use templates to customize this field. Maximum length is 255 characters.", + "placeholder": "{{ template \"jira.default.summary\" . }}", + "propertyName": "summary", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "text", + "label": "Description", + "description": "The description of the Jira issue. You can use templates to customize this field. Maximum length is 32767 characters.", + "placeholder": "{{ template \"jira.default.description\" . }}", + "propertyName": "description", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "string_array", + "inputType": "", + "label": "Labels", + "description": "Labels to assign to the Jira issue. You can use templates to customize this field.", + "placeholder": "", + "propertyName": "labels", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Priority", + "description": "The priority of the Jira issue (e.g., High, Medium, Low). You can use templates to customize this field.", + "placeholder": "{{ template \"jira.default.priority\" . }}", + "propertyName": "priority", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Resolve Transition", + "description": "Name of the workflow transition to resolve an issue. The target status must have the category \"done\". If not set, the issue will not be resolved.", + "placeholder": "", + "propertyName": "resolve_transition", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Reopen Transition", + "description": "Name of the workflow transition to resolve an issue. The target status must not have the category \"done\". If not set, the issue will not be reopened.", + "placeholder": "", + "propertyName": "reopen_transition", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Reopen Duration", + "description": "Reopen the issue when it is not older than this value in minutes. Otherwise, create a new issue.", + "placeholder": "10m", + "propertyName": "reopen_duration", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "\"Won't fix\" Transition", + "description": "If reopen transition is defined, ignore issues with that resolution.", + "placeholder": "", + "propertyName": "wont_fix_resolution", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Custom field ID for deduplication", + "description": "Id of the custom field where the deduplication key should be stored. Otherwise, it is added to labels in format 'ALERT($KEY).'", + "placeholder": "10000", + "propertyName": "dedup_key_field", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "^[0-9]+$", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "key_value_map", + "inputType": "text", + "label": "Custom Field Data", + "description": "Custom field data to set on the Jira issue.", + "placeholder": "", + "propertyName": "fields", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + } + ] + }, + { + "type": "kafka", + "name": "Kafka REST Proxy", + "heading": "Kafka settings", + "description": "Sends notifications to Kafka Rest Proxy", + "info": "", + "options": [ + { + "element": "input", + "inputType": "text", + "label": "Kafka REST Proxy", + "description": "Hint: If you are directly using v3 APIs hosted on a Confluent Kafka Server, you must append /kafka to the URL here. Example: https://localhost:8082/kafka", + "placeholder": "http://localhost:8082", + "propertyName": "kafkaRestProxy", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Topic", + "description": "", + "placeholder": "topic1", + "propertyName": "kafkaTopic", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Username", + "description": "", + "placeholder": "", + "propertyName": "username", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "password", + "label": "Password", + "description": "The password to use when making a call to the Kafka REST Proxy", + "placeholder": "", + "propertyName": "password", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "select", + "inputType": "text", + "label": "API version", + "description": "The API version to use when contacting the Kafka REST Server. By default v2 will be used.", + "placeholder": "", + "propertyName": "apiVersion", + "selectOptions": [ + { + "value": "v2", + "label": "v2" + }, + { + "value": "v3", + "label": "v3" + } + ], + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Cluster ID", + "description": "v3 APIs require a clusterID to be specified.", + "placeholder": "lkc-abcde", + "propertyName": "kafkaClusterId", + "selectOptions": null, + "showWhen": { + "field": "apiVersion", + "is": "v3" + }, + "required": true, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "text", + "label": "Description", + "description": "Templated description of the Kafka message", + "placeholder": "{{ template \"default.title\" . }}", + "propertyName": "description", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "", + "label": "Details", + "description": "Custom details to include with the message. You can use template variables.", + "placeholder": "{{ template \"default.message\" . }}", + "propertyName": "details", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + } + ] + }, + { + "type": "mqtt", + "name": "MQTT", + "heading": "MQTT settings", + "description": "Sends notifications to an MQTT broker", + "info": "The MQTT notifier sends messages to an MQTT broker. The message is sent to the topic specified in the configuration. ", + "options": [ + { + "element": "input", + "inputType": "text", + "label": "Broker URL", + "description": "The URL of the MQTT broker.", + "placeholder": "tcp://localhost:1883", + "propertyName": "brokerUrl", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Topic", + "description": "The topic to which the message will be sent.", + "placeholder": "grafana/alerts", + "propertyName": "topic", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "select", + "inputType": "text", + "label": "Message format", + "description": "If set to 'json', the notification message is the default JSON payload, and the Message field sets only the message field in the payload. If set to 'text', the Message field defines the entire payload. The default is 'json'.", + "placeholder": "json", + "propertyName": "messageFormat", + "selectOptions": [ + { + "value": "json", + "label": "json" + }, + { + "value": "text", + "label": "text" + } + ], + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Client ID", + "description": "The client ID to use when connecting to the MQTT broker. If blank, a random client ID is used.", + "placeholder": "", + "propertyName": "clientId", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "", + "label": "Message", + "description": "In 'json' Message format, sets the message field of the default JSON payload. In 'text' Message format, defines the entire payload.", + "placeholder": "{{ template \"default.message\" . }}", + "propertyName": "message", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Username", + "description": "The username to use when connecting to the MQTT broker.", + "placeholder": "", + "propertyName": "username", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Password", + "description": "The password to use when connecting to the MQTT broker.", + "placeholder": "", + "propertyName": "password", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "select", + "inputType": "", + "label": "QoS", + "description": "The quality of service to use when sending the message.", + "placeholder": "", + "propertyName": "qos", + "selectOptions": [ + { + "value": "0", + "label": "At most once (0)" + }, + { + "value": "1", + "label": "At least once (1)" + }, + { + "value": "2", + "label": "Exactly once (2)" + } + ], + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "checkbox", + "inputType": "", + "label": "Retain", + "description": "If set to true, the message will be retained by the broker.", + "placeholder": "", + "propertyName": "retain", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "subform", + "inputType": "", + "label": "TLS", + "description": "TLS configuration options", + "placeholder": "", + "propertyName": "tlsConfig", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": [ + { + "element": "checkbox", + "inputType": "", + "label": "Disable certificate verification", + "description": "Do not verify the broker's certificate chain and host name.", + "placeholder": "", + "propertyName": "insecureSkipVerify", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "text", + "label": "CA Certificate", + "description": "Certificate in PEM format to use when verifying the broker's certificate chain.", + "placeholder": "", + "propertyName": "caCertificate", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "text", + "label": "Client Certificate", + "description": "Client certificate in PEM format to use when connecting to the broker.", + "placeholder": "", + "propertyName": "clientCertificate", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "text", + "label": "Client Key", + "description": "Client key in PEM format to use when connecting to the broker.", + "placeholder": "", + "propertyName": "clientKey", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + } + ] + } + ] + }, + { + "type": "oncall", + "name": "Grafana IRM", + "heading": "Grafana IRM settings", + "description": "Sends alerts to Grafana IRM", + "info": "", + "options": [ + { + "element": "input", + "inputType": "text", + "label": "URL", + "description": "", + "placeholder": "", + "propertyName": "url", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "select", + "inputType": "", + "label": "HTTP Method", + "description": "", + "placeholder": "", + "propertyName": "httpMethod", + "selectOptions": [ + { + "value": "POST", + "label": "POST" + }, + { + "value": "PUT", + "label": "PUT" + } + ], + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "HTTP Basic Authentication - Username", + "description": "", + "placeholder": "", + "propertyName": "username", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "password", + "label": "HTTP Basic Authentication - Password", + "description": "", + "placeholder": "", + "propertyName": "password", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Authorization Header - Scheme", + "description": "Optionally provide a scheme for the Authorization Request Header. Default is Bearer.", + "placeholder": "Bearer", + "propertyName": "authorization_scheme", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Authorization Header - Credentials", + "description": "Credentials for the Authorization Request header. Only one of HTTP Basic Authentication or Authorization Request Header can be set.", + "placeholder": "", + "propertyName": "authorization_credentials", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Max Alerts", + "description": "Max alerts to include in a notification. Remaining alerts in the same batch will be ignored above this number. 0 means no limit.", + "placeholder": "", + "propertyName": "maxAlerts", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "text", + "label": "Title", + "description": "Templated title of the message.", + "placeholder": "{{ template \"default.title\" . }}", + "propertyName": "title", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "", + "label": "Message", + "description": "Custom message. You can use template variables.", + "placeholder": "{{ template \"default.message\" . }}", + "propertyName": "message", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + } + ] + }, + { + "type": "opsgenie", + "name": "OpsGenie", + "heading": "OpsGenie settings", + "description": "Sends notifications to OpsGenie", + "info": "", + "options": [ + { + "element": "input", + "inputType": "text", + "label": "API Key", + "description": "", + "placeholder": "OpsGenie API Key", + "propertyName": "apiKey", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Alert API URL", + "description": "", + "placeholder": "https://api.opsgenie.com/v2/alerts", + "propertyName": "apiUrl", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "text", + "label": "Message", + "description": "Alert text limited to 130 characters.", + "placeholder": "{{ template \"default.title\" . }}", + "propertyName": "message", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "", + "label": "Description", + "description": "A description of the incident.", + "placeholder": "", + "propertyName": "description", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "checkbox", + "inputType": "", + "label": "Auto close incidents", + "description": "Automatically close alerts in OpsGenie once the alert goes back to ok.", + "placeholder": "", + "propertyName": "autoClose", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "checkbox", + "inputType": "", + "label": "Override priority", + "description": "Allow the alert priority to be set using the og_priority label.", + "placeholder": "", + "propertyName": "overridePriority", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "select", + "inputType": "", + "label": "Send notification tags as", + "description": "Send the common annotations to Opsgenie as either Extra Properties, Tags or both", + "placeholder": "", + "propertyName": "sendTagsAs", + "selectOptions": [ + { + "value": "tags", + "label": "Tags" + }, + { + "value": "details", + "label": "Extra Properties" + }, + { + "value": "both", + "label": "Tags \u0026 Extra Properties" + } + ], + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "subform_array", + "inputType": "", + "label": "Responders", + "description": "If the API key belongs to a team, this field is ignored.", + "placeholder": "", + "propertyName": "responders", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": [ + { + "element": "input", + "inputType": "", + "label": "Type", + "description": "team, teams, user, escalation, schedule or a template", + "placeholder": "", + "propertyName": "type", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "", + "label": "Name", + "description": "Name of the responder. Must be specified if ID and Username are empty or if the type is 'teams'.", + "placeholder": "", + "propertyName": "name", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "", + "label": "ID", + "description": "ID of the responder. Must be specified if name and Username are empty.", + "placeholder": "", + "propertyName": "id", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "", + "label": "Username", + "description": "User name of the responder. Must be specified if ID and Name are empty.", + "placeholder": "", + "propertyName": "username", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + } + ] + } + ] + }, + { + "type": "pagerduty", + "name": "PagerDuty", + "heading": "PagerDuty settings", + "description": "Sends notifications to PagerDuty", + "info": "", + "options": [ + { + "element": "input", + "inputType": "text", + "label": "Integration Key", + "description": "", + "placeholder": "Pagerduty Integration Key", + "propertyName": "integrationKey", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Severity", + "description": "Severity of the event. It must be critical, error, warning, info - otherwise, the default is set which is critical. You can use templates", + "placeholder": "critical", + "propertyName": "severity", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Class", + "description": "The class/type of the event, for example 'ping failure' or 'cpu load'", + "placeholder": "", + "propertyName": "class", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Component", + "description": "Component of the source machine that is responsible for the event, for example mysql or eth0", + "placeholder": "Grafana", + "propertyName": "component", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Group", + "description": "Logical grouping of components of a service, for example 'app-stack'", + "placeholder": "", + "propertyName": "group", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "text", + "label": "Summary", + "description": "You can use templates for summary", + "placeholder": "{{ template \"default.title\" . }}", + "propertyName": "summary", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Source", + "description": "The unique location of the affected system, preferably a hostname or FQDN. You can use templates", + "placeholder": "grafana.local", + "propertyName": "source", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Client", + "description": "The name of the monitoring client that is triggering this event. You can use templates", + "placeholder": "Grafana", + "propertyName": "client", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Client URL", + "description": "The URL of the monitoring client that is triggering this event. You can use templates", + "placeholder": "{{ .ExternalURL }}", + "propertyName": "client_url", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "key_value_map", + "inputType": "text", + "label": "Details", + "description": "A set of arbitrary key/value pairs that provide further detail about the incident.", + "placeholder": "", + "propertyName": "details", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "URL", + "description": "The URL to send API requests to", + "placeholder": "https://events.pagerduty.com/v2/enqueue", + "propertyName": "url", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + } + ] + }, + { + "type": "prometheus-alertmanager", + "name": "Alertmanager", + "heading": "Alertmanager Settings", + "description": "Sends notifications to Alertmanager", + "info": "", + "options": [ + { + "element": "input", + "inputType": "text", + "label": "URL", + "description": "", + "placeholder": "http://localhost:9093", + "propertyName": "url", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Basic Auth User", + "description": "", + "placeholder": "", + "propertyName": "basicAuthUser", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "password", + "label": "Basic Auth Password", + "description": "", + "placeholder": "", + "propertyName": "basicAuthPassword", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + } + ] + }, + { + "type": "pushover", + "name": "Pushover", + "heading": "Pushover settings", + "description": "Sends HTTP POST request to the Pushover API", + "info": "", + "options": [ + { + "element": "input", + "inputType": "text", + "label": "API Token", + "description": "", + "placeholder": "Application token", + "propertyName": "apiToken", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "User key(s)", + "description": "", + "placeholder": "comma-separated list", + "propertyName": "userKey", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Device(s) (optional)", + "description": "", + "placeholder": "comma-separated list; leave empty to send to all devices", + "propertyName": "device", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "select", + "inputType": "", + "label": "Alerting priority", + "description": "", + "placeholder": "", + "propertyName": "priority", + "selectOptions": [ + { + "value": "2", + "label": "Emergency" + }, + { + "value": "1", + "label": "High" + }, + { + "value": "0", + "label": "Normal" + }, + { + "value": "-1", + "label": "Low" + }, + { + "value": "-2", + "label": "Lowest" + } + ], + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "select", + "inputType": "", + "label": "OK priority", + "description": "", + "placeholder": "", + "propertyName": "okPriority", + "selectOptions": [ + { + "value": "2", + "label": "Emergency" + }, + { + "value": "1", + "label": "High" + }, + { + "value": "0", + "label": "Normal" + }, + { + "value": "-1", + "label": "Low" + }, + { + "value": "-2", + "label": "Lowest" + } + ], + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Retry (Only used for Emergency Priority)", + "description": "How often (in seconds) the Pushover servers will send the same alerting or OK notification to the user.", + "placeholder": "minimum 30 seconds", + "propertyName": "retry", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Expire (Only used for Emergency Priority)", + "description": "How many seconds the alerting or OK notification will continue to be retried.", + "placeholder": "maximum 10800 seconds", + "propertyName": "expire", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "select", + "inputType": "", + "label": "Alerting sound", + "description": "", + "placeholder": "", + "propertyName": "sound", + "selectOptions": [ + { + "value": "default", + "label": "Default" + }, + { + "value": "pushover", + "label": "Pushover" + }, + { + "value": "bike", + "label": "Bike" + }, + { + "value": "bugle", + "label": "Bugle" + }, + { + "value": "cashregister", + "label": "Cashregister" + }, + { + "value": "classical", + "label": "Classical" + }, + { + "value": "cosmic", + "label": "Cosmic" + }, + { + "value": "falling", + "label": "Falling" + }, + { + "value": "gamelan", + "label": "Gamelan" + }, + { + "value": "incoming", + "label": "Incoming" + }, + { + "value": "intermission", + "label": "Intermission" + }, + { + "value": "magic", + "label": "Magic" + }, + { + "value": "mechanical", + "label": "Mechanical" + }, + { + "value": "pianobar", + "label": "Pianobar" + }, + { + "value": "siren", + "label": "Siren" + }, + { + "value": "spacealarm", + "label": "Spacealarm" + }, + { + "value": "tugboat", + "label": "Tugboat" + }, + { + "value": "alien", + "label": "Alien" + }, + { + "value": "climb", + "label": "Climb" + }, + { + "value": "persistent", + "label": "Persistent" + }, + { + "value": "echo", + "label": "Echo" + }, + { + "value": "updown", + "label": "Updown" + }, + { + "value": "none", + "label": "None" + } + ], + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "select", + "inputType": "", + "label": "OK sound", + "description": "", + "placeholder": "", + "propertyName": "okSound", + "selectOptions": [ + { + "value": "default", + "label": "Default" + }, + { + "value": "pushover", + "label": "Pushover" + }, + { + "value": "bike", + "label": "Bike" + }, + { + "value": "bugle", + "label": "Bugle" + }, + { + "value": "cashregister", + "label": "Cashregister" + }, + { + "value": "classical", + "label": "Classical" + }, + { + "value": "cosmic", + "label": "Cosmic" + }, + { + "value": "falling", + "label": "Falling" + }, + { + "value": "gamelan", + "label": "Gamelan" + }, + { + "value": "incoming", + "label": "Incoming" + }, + { + "value": "intermission", + "label": "Intermission" + }, + { + "value": "magic", + "label": "Magic" + }, + { + "value": "mechanical", + "label": "Mechanical" + }, + { + "value": "pianobar", + "label": "Pianobar" + }, + { + "value": "siren", + "label": "Siren" + }, + { + "value": "spacealarm", + "label": "Spacealarm" + }, + { + "value": "tugboat", + "label": "Tugboat" + }, + { + "value": "alien", + "label": "Alien" + }, + { + "value": "climb", + "label": "Climb" + }, + { + "value": "persistent", + "label": "Persistent" + }, + { + "value": "echo", + "label": "Echo" + }, + { + "value": "updown", + "label": "Updown" + }, + { + "value": "none", + "label": "None" + } + ], + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "text", + "label": "Title", + "description": "", + "placeholder": "{{ template \"default.title\" . }}", + "propertyName": "title", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "", + "label": "Message", + "description": "", + "placeholder": "{{ template \"default.message\" . }}", + "propertyName": "message", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + } + ] + }, + { + "type": "sensugo", + "name": "Sensu Go", + "heading": "Sensu Go Settings", + "description": "Sends HTTP POST request to a Sensu Go API", + "info": "", + "options": [ + { + "element": "input", + "inputType": "text", + "label": "Backend URL", + "description": "", + "placeholder": "http://sensu-api.local:8080", + "propertyName": "url", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "password", + "label": "API Key", + "description": "API key to auth to Sensu Go backend", + "placeholder": "", + "propertyName": "apikey", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Proxy entity name", + "description": "", + "placeholder": "default", + "propertyName": "entity", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Check name", + "description": "", + "placeholder": "default", + "propertyName": "check", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Handler", + "description": "", + "placeholder": "", + "propertyName": "handler", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Namespace", + "description": "", + "placeholder": "default", + "propertyName": "namespace", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "", + "label": "Message", + "description": "", + "placeholder": "{{ template \"default.message\" . }}", + "propertyName": "message", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + } + ] + }, + { + "type": "slack", + "name": "Slack", + "heading": "Slack settings", + "description": "Sends notifications to Slack", + "info": "", + "options": [ + { + "element": "input", + "inputType": "text", + "label": "Recipient", + "description": "Specify channel, private group, or IM channel (can be an encoded ID or a name) - required unless you provide a webhook", + "placeholder": "", + "propertyName": "recipient", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": false, + "dependsOn": "url", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Token", + "description": "Provide a Slack API token (starts with \"xoxb\") - required unless you provide a webhook", + "placeholder": "", + "propertyName": "token", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": true, + "dependsOn": "url", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Username", + "description": "Set the username for the bot's message", + "placeholder": "", + "propertyName": "username", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Icon emoji", + "description": "Provide an emoji to use as the icon for the bot's message. Overrides the icon URL.", + "placeholder": "", + "propertyName": "icon_emoji", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Icon URL", + "description": "Provide a URL to an image to use as the icon for the bot's message", + "placeholder": "", + "propertyName": "icon_url", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Mention Users", + "description": "Mention one or more users (comma separated) when notifying in a channel, by ID (you can copy this from the user's Slack profile)", + "placeholder": "", + "propertyName": "mentionUsers", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Mention Groups", + "description": "Mention one or more groups (comma separated) when notifying in a channel (you can copy this from the group's Slack profile URL)", + "placeholder": "", + "propertyName": "mentionGroups", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "select", + "inputType": "", + "label": "Mention Channel", + "description": "Mention whole channel or just active members when notifying", + "placeholder": "", + "propertyName": "mentionChannel", + "selectOptions": [ + { + "value": "", + "label": "Disabled" + }, + { + "value": "here", + "label": "Every active channel member" + }, + { + "value": "channel", + "label": "Every channel member" + } + ], + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Webhook URL", + "description": "Optionally provide a Slack incoming webhook URL for sending messages, in this case the token isn't necessary", + "placeholder": "Slack incoming webhook URL", + "propertyName": "url", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": true, + "dependsOn": "token", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Endpoint URL", + "description": "Optionally provide a custom Slack message API endpoint for non-webhook requests, default is https://slack.com/api/chat.postMessage", + "placeholder": "Slack endpoint url", + "propertyName": "endpointUrl", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Color", + "description": "Templated color of the slack message", + "placeholder": "{{ if eq .Status \"firing\" }}#D63232{{ else }}#36a64f{{ end }}", + "propertyName": "color", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Title", + "description": "Templated title of the slack message", + "placeholder": "{{ template \"slack.default.title\" . }}", + "propertyName": "title", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "", + "label": "Text Body", + "description": "Body of the slack message", + "placeholder": "{{ template \"slack.default.text\" . }}", + "propertyName": "text", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + } + ] + }, + { + "type": "sns", + "name": "AWS SNS", + "heading": "AWS SNS settings", + "description": "Sends notifications to AWS Simple Notification Service", + "info": "", + "options": [ + { + "element": "input", + "inputType": "text", + "label": "The Amazon SNS API URL", + "description": "", + "placeholder": "", + "propertyName": "api_url", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "subform", + "inputType": "", + "label": "SigV4 Authentication", + "description": "Configures AWS's Signature Verification 4 signing process to sign requests", + "placeholder": "", + "propertyName": "sigv4", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": [ + { + "element": "input", + "inputType": "text", + "label": "Region", + "description": "The AWS region. If blank, the region from the default credentials chain is used.", + "placeholder": "", + "propertyName": "region", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Access Key", + "description": "The AWS API access key.", + "placeholder": "", + "propertyName": "access_key", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Secret Key", + "description": "The AWS API secret key.", + "placeholder": "", + "propertyName": "secret_key", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Profile", + "description": "Named AWS profile used to authenticate", + "placeholder": "", + "propertyName": "profile", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Role ARN", + "description": "AWS Role ARN, an alternative to using AWS API keys", + "placeholder": "", + "propertyName": "role_arn", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + } + ] + }, + { + "element": "input", + "inputType": "text", + "label": "SNS topic ARN", + "description": "If you don't specify this value, you must specify a value for the phone_number or target_arn. If you are using a FIFO SNS topic you should set a message group interval longer than 5 minutes to prevent messages with the same group key being deduplicated by the SNS default deduplication window.", + "placeholder": "", + "propertyName": "topic_arn", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Phone number", + "description": "Phone number if message is delivered via SMS in E.164 format. If you don't specify this value, you must specify a value for the topic_arn or target_arn", + "placeholder": "", + "propertyName": "phone_number", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Target ARN", + "description": "The mobile platform endpoint ARN if message is delivered via mobile notifications. If you don't specify this value, you must specify a value for the topic_arn or phone_number", + "placeholder": "", + "propertyName": "target_arn", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "text", + "label": "Subject", + "description": "Optional subject. By default, this field uses the default title template and can be customized with templates and custom messages. It cannot be an empty string", + "placeholder": "{{ template \"default.title\" . }}", + "propertyName": "subject", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "", + "label": "Message", + "description": "Optional message. By default, this field uses the default message template and can be customized with templates and custom messages", + "placeholder": "{{ template \"default.message\" . }}", + "propertyName": "message", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "key_value_map", + "inputType": "text", + "label": "Attributes", + "description": "SNS message attributes", + "placeholder": "", + "propertyName": "attributes", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + } + ] + }, + { + "type": "teams", + "name": "Microsoft Teams", + "heading": "Teams settings", + "description": "Sends notifications using Incoming Webhook connector to Microsoft Teams", + "info": "", + "options": [ + { + "element": "input", + "inputType": "text", + "label": "URL", + "description": "", + "placeholder": "Teams incoming webhook url", + "propertyName": "url", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "text", + "label": "Title", + "description": "Templated title of the Teams message.", + "placeholder": "{{ template \"default.title\" . }}", + "propertyName": "title", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Section Title", + "description": "Section title for the Teams message. Leave blank for none.", + "placeholder": "", + "propertyName": "sectiontitle", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "", + "label": "Message", + "description": "", + "placeholder": "{{ template \"default.message\" . }}", + "propertyName": "message", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + } + ] + }, + { + "type": "telegram", + "name": "Telegram", + "heading": "Telegram API settings", + "description": "Sends notifications to Telegram", + "info": "", + "options": [ + { + "element": "input", + "inputType": "text", + "label": "BOT API Token", + "description": "", + "placeholder": "Telegram BOT API Token", + "propertyName": "bottoken", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Chat ID", + "description": "Integer Telegram Chat Identifier", + "placeholder": "", + "propertyName": "chatid", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Message Thread ID", + "description": "Integer Telegram Message Thread Identifier", + "placeholder": "", + "propertyName": "message_thread_id", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "-?[0-9]{1,10}", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "", + "label": "Message", + "description": "", + "placeholder": "{{ template \"default.message\" . }}", + "propertyName": "message", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "select", + "inputType": "", + "label": "Parse Mode", + "description": "Mode for parsing entities in the message text. Default is 'HTML'", + "placeholder": "", + "propertyName": "parse_mode", + "selectOptions": [ + { + "value": "None", + "label": "None" + }, + { + "value": "HTML", + "label": "HTML" + }, + { + "value": "Markdown", + "label": "Markdown" + }, + { + "value": "MarkdownV2", + "label": "Markdown V2" + } + ], + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "checkbox", + "inputType": "", + "label": "Disable Web Page Preview", + "description": "Disables link previews for links in this message", + "placeholder": "", + "propertyName": "disable_web_page_preview", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "checkbox", + "inputType": "", + "label": "Protect Content", + "description": "Protects the contents of the sent message from forwarding and saving", + "placeholder": "", + "propertyName": "protect_content", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "checkbox", + "inputType": "", + "label": "Disable Notification", + "description": "Sends the message silently. Users will receive a notification with no sound.", + "placeholder": "", + "propertyName": "disable_notification", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + } + ] + }, + { + "type": "threema", + "name": "Threema Gateway", + "heading": "Threema Gateway settings", + "description": "Sends notifications to Threema using Threema Gateway (Basic IDs)", + "info": "Notifications can be configured for any Threema Gateway ID of type \"Basic\". End-to-End IDs are not currently supported.The Threema Gateway ID can be set up at https://gateway.threema.ch/.", + "options": [ + { + "element": "input", + "inputType": "text", + "label": "Gateway ID", + "description": "Your 8 character Threema Gateway Basic ID (starting with a *).", + "placeholder": "*3MAGWID", + "propertyName": "gateway_id", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "\\*[0-9A-Z]{7}", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Recipient ID", + "description": "The 8 character Threema ID that should receive the alerts.", + "placeholder": "YOUR3MID", + "propertyName": "recipient_id", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "[0-9A-Z]{8}", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "API Secret", + "description": "Your Threema Gateway API secret.", + "placeholder": "", + "propertyName": "api_secret", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "text", + "label": "Title", + "description": "Templated title of the message.", + "placeholder": "{{ template \"default.title\" . }}", + "propertyName": "title", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "text", + "label": "Description", + "description": "Templated description of the message.", + "placeholder": "{{ template \"default.message\" . }}", + "propertyName": "description", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + } + ] + }, + { + "type": "victorops", + "name": "VictorOps", + "heading": "VictorOps settings", + "description": "Sends notifications to VictorOps", + "info": "", + "options": [ + { + "element": "input", + "inputType": "text", + "label": "URL", + "description": "", + "placeholder": "VictorOps url", + "propertyName": "url", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "select", + "inputType": "", + "label": "Message Type", + "description": "", + "placeholder": "", + "propertyName": "messageType", + "selectOptions": [ + { + "value": "CRITICAL", + "label": "CRITICAL" + }, + { + "value": "WARNING", + "label": "WARNING" + } + ], + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "text", + "label": "Title", + "description": "Templated title to display", + "placeholder": "{{ template \"default.title\" . }}", + "propertyName": "title", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "text", + "label": "Description", + "description": "Templated description of the message", + "placeholder": "{{ template \"default.message\" . }}", + "propertyName": "description", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + } + ] + }, + { + "type": "webex", + "name": "Cisco Webex Teams", + "heading": "Webex settings", + "description": "Sends notifications to Cisco Webex Teams", + "info": "Notifications can be configured for any Cisco Webex Teams", + "options": [ + { + "element": "input", + "inputType": "text", + "label": "Cisco Webex API URL", + "description": "API endpoint at which we'll send webhooks to.", + "placeholder": "https://api.ciscospark.com/v1/messages", + "propertyName": "api_url", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Room ID", + "description": "The room ID to send messages to.", + "placeholder": "GMtOWY0ZGJkNzMyMGFl", + "propertyName": "room_id", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Bot Token", + "description": "Non-expiring access token of the bot that will post messages on our behalf.", + "placeholder": "GMtOWY0ZGJkNzMyMGFl-12535454-123213", + "propertyName": "bot_token", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Notification Template", + "description": "Notification template to use. Markdown is supported.", + "placeholder": "{{ template \"default.message\" . }}", + "propertyName": "message", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + } + ] + }, + { + "type": "webhook", + "name": "Webhook", + "heading": "Webhook settings", + "description": "Sends HTTP POST request to a URL", + "info": "", + "options": [ + { + "element": "input", + "inputType": "text", + "label": "URL", + "description": "", + "placeholder": "", + "propertyName": "url", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "select", + "inputType": "", + "label": "HTTP Method", + "description": "", + "placeholder": "", + "propertyName": "httpMethod", + "selectOptions": [ + { + "value": "POST", + "label": "POST" + }, + { + "value": "PUT", + "label": "PUT" + } + ], + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "HTTP Basic Authentication - Username", + "description": "", + "placeholder": "", + "propertyName": "username", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "password", + "label": "HTTP Basic Authentication - Password", + "description": "", + "placeholder": "", + "propertyName": "password", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Authorization Header - Scheme", + "description": "Optionally provide a scheme for the Authorization Request Header. Default is Bearer.", + "placeholder": "Bearer", + "propertyName": "authorization_scheme", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Authorization Header - Credentials", + "description": "Credentials for the Authorization Request header. Only one of HTTP Basic Authentication or Authorization Request Header can be set.", + "placeholder": "", + "propertyName": "authorization_credentials", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "key_value_map", + "inputType": "text", + "label": "Extra Headers", + "description": "Optionally provide extra headers to be used in the request.", + "placeholder": "", + "propertyName": "headers", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Max Alerts", + "description": "Max alerts to include in a notification. Remaining alerts in the same batch will be ignored above this number. 0 means no limit.", + "placeholder": "", + "propertyName": "maxAlerts", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "text", + "label": "Title", + "description": "Templated title of the message.", + "placeholder": "{{ template \"default.title\" . }}", + "propertyName": "title", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "", + "label": "Message", + "description": "Templated message to be used in the payload's \"message\" field.", + "placeholder": "{{ template \"default.message\" . }}", + "propertyName": "message", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "subform", + "inputType": "", + "label": "Custom Payload", + "description": "Optionally provide a templated payload. Overrides 'Message' and 'Title' field.", + "placeholder": "", + "propertyName": "payload", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": [ + { + "element": "textarea", + "inputType": "", + "label": "Payload Template", + "description": "Custom payload template.", + "placeholder": "{{ template \"webhook.default.payload\" . }}", + "propertyName": "template", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "key_value_map", + "inputType": "text", + "label": "Payload Variables", + "description": "Optionally provide a variables to be used in the payload template. They will be available in the template as `.Vars.\u003cvariable_name\u003e`.", + "placeholder": "", + "propertyName": "vars", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + } + ] + }, + { + "element": "subform", + "inputType": "", + "label": "TLS", + "description": "TLS configuration options", + "placeholder": "", + "propertyName": "tlsConfig", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": [ + { + "element": "checkbox", + "inputType": "", + "label": "Disable certificate verification", + "description": "Do not verify the server's certificate chain and host name.", + "placeholder": "", + "propertyName": "insecureSkipVerify", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "text", + "label": "CA Certificate", + "description": "Certificate in PEM format to use when verifying the server's certificate chain.", + "placeholder": "", + "propertyName": "caCertificate", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "text", + "label": "Client Certificate", + "description": "Client certificate in PEM format to use when connecting to the server.", + "placeholder": "", + "propertyName": "clientCertificate", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "text", + "label": "Client Key", + "description": "Client key in PEM format to use when connecting to the server.", + "placeholder": "", + "propertyName": "clientKey", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + } + ] + }, + { + "element": "subform", + "inputType": "", + "label": "HMAC Signature", + "description": "HMAC signature configuration options", + "placeholder": "", + "propertyName": "hmacConfig", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": [ + { + "element": "input", + "inputType": "text", + "label": "Secret", + "description": "", + "placeholder": "", + "propertyName": "secret", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Header", + "description": "The header in which the HMAC signature will be included.", + "placeholder": "X-Grafana-Alerting-Signature", + "propertyName": "header", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Timestamp header", + "description": "If set, the timestamp will be included in the HMAC signature. The value should be the name of the header to use.", + "placeholder": "", + "propertyName": "timestampHeader", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + } + ] + }, + { + "element": "subform", + "inputType": "", + "label": "HTTP Config", + "description": "Common HTTP client options.", + "placeholder": "", + "propertyName": "http_config", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": [ + { + "element": "subform", + "inputType": "", + "label": "OAuth2", + "description": "OAuth2 configuration options", + "placeholder": "", + "propertyName": "oauth2", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": [ + { + "element": "input", + "inputType": "text", + "label": "Token URL", + "description": "URL for the access token endpoint.", + "placeholder": "", + "propertyName": "token_url", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Client ID", + "description": "Client ID to use when authenticating.", + "placeholder": "", + "propertyName": "client_id", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Client Secret", + "description": "Client secret to use when authenticating.", + "placeholder": "", + "propertyName": "client_secret", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "string_array", + "inputType": "", + "label": "Scopes", + "description": "Optional scopes to request when obtaining an access token.", + "placeholder": "", + "propertyName": "scopes", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "key_value_map", + "inputType": "", + "label": "Endpoint Parameters", + "description": "Optional parameters to append to the access token request.", + "placeholder": "", + "propertyName": "endpoint_params", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "subform", + "inputType": "", + "label": "TLS", + "description": "Optional TLS configuration options for OAuth2 requests.", + "placeholder": "", + "propertyName": "tls_config", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": [ + { + "element": "checkbox", + "inputType": "", + "label": "Disable certificate verification", + "description": "Do not verify the server's certificate chain and host name.", + "placeholder": "", + "propertyName": "insecureSkipVerify", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "text", + "label": "CA Certificate", + "description": "Certificate in PEM format to use when verifying the server's certificate chain.", + "placeholder": "", + "propertyName": "caCertificate", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "text", + "label": "Client Certificate", + "description": "Client certificate in PEM format to use when connecting to the server.", + "placeholder": "", + "propertyName": "clientCertificate", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "text", + "label": "Client Key", + "description": "Client key in PEM format to use when connecting to the server.", + "placeholder": "", + "propertyName": "clientKey", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": true, + "dependsOn": "", + "subformOptions": null + } + ] + }, + { + "element": "subform", + "inputType": "", + "label": "Proxy Config", + "description": "Optional proxy configuration.", + "placeholder": "", + "propertyName": "proxy_config", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": [ + { + "element": "input", + "inputType": "text", + "label": "Proxy URL", + "description": "HTTP proxy server to use to connect to the targets.", + "placeholder": "https://proxy.example.com", + "propertyName": "proxy_url", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "checkbox", + "inputType": "", + "label": "Proxy from environment", + "description": "Use environment HTTP_PROXY, HTTPS_PROXY and NO_PROXY to determine proxies.", + "placeholder": "", + "propertyName": "proxy_from_environment", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "No Proxy", + "description": "Comma-separated list of addresses that should not use a proxy.", + "placeholder": "example.com,1.2.3.4", + "propertyName": "no_proxy", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "key_value_map", + "inputType": "text", + "label": "Proxy Connect Header", + "description": "Optional headers to send to proxies during CONNECT requests.", + "placeholder": "", + "propertyName": "proxy_connect_header", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + } + ] + } + ] + } + ] + } + ] + }, + { + "type": "wecom", + "name": "WeCom", + "heading": "WeCom settings", + "description": "Send alerts generated by Grafana to WeCom", + "info": "", + "options": [ + { + "element": "input", + "inputType": "text", + "label": "Webhook URL", + "description": "Required if using GroupRobot", + "placeholder": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxxxxxx", + "propertyName": "url", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": true, + "dependsOn": "secret", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Agent ID", + "description": "Required if using APIAPP, see https://work.weixin.qq.com/wework_admin/frame#apps create ApiApp", + "placeholder": "1000002", + "propertyName": "agent_id", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": false, + "dependsOn": "url", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "Corp ID", + "description": "Required if using APIAPP, see https://work.weixin.qq.com/wework_admin/frame#profile", + "placeholder": "wwxxxxxxxxx", + "propertyName": "corp_id", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": false, + "dependsOn": "url", + "subformOptions": null + }, + { + "element": "input", + "inputType": "password", + "label": "Secret", + "description": "Required if using APIAPP", + "placeholder": "secret", + "propertyName": "secret", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": true, + "validationRule": "", + "secure": true, + "dependsOn": "url", + "subformOptions": null + }, + { + "element": "select", + "inputType": "", + "label": "Message Type", + "description": "", + "placeholder": "Text", + "propertyName": "msgtype", + "selectOptions": [ + { + "value": "text", + "label": "Text" + }, + { + "value": "markdown", + "label": "Markdown" + } + ], + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "", + "label": "Message", + "description": "Custom WeCom message. You can use template variables.", + "placeholder": "{{ template \"default.message\" . }}", + "propertyName": "message", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "textarea", + "inputType": "text", + "label": "Title", + "description": "Templated title of the message", + "placeholder": "{{ template \"default.title\" . }}", + "propertyName": "title", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + }, + { + "element": "input", + "inputType": "text", + "label": "To User", + "description": "", + "placeholder": "@all", + "propertyName": "touser", + "selectOptions": null, + "showWhen": { + "field": "", + "is": "" + }, + "required": false, + "validationRule": "", + "secure": false, + "dependsOn": "", + "subformOptions": null + } + ] + } +] \ No newline at end of file diff --git a/pkg/tests/api/alerting/test-data/alert-notifiers-v2-snapshot.json b/pkg/tests/api/alerting/test-data/alert-notifiers-v2-snapshot.json index fbf9e0fad82..cd5ed8b8f96 100644 --- a/pkg/tests/api/alerting/test-data/alert-notifiers-v2-snapshot.json +++ b/pkg/tests/api/alerting/test-data/alert-notifiers-v2-snapshot.json @@ -7,6 +7,7 @@ "description": "Send notifications to LINE notify", "versions": [ { + "typeAlias": "line", "version": "v1", "canCreate": true, "options": [ @@ -8143,7 +8144,7 @@ "type": "sns", "currentVersion": "v1", "name": "AWS SNS", - "heading": "Webex settings", + "heading": "AWS SNS settings", "description": "Sends notifications to AWS Simple Notification Service", "versions": [ { diff --git a/pkg/tests/apis/alerting/notifications/receivers/receiver_test.go b/pkg/tests/apis/alerting/notifications/receivers/receiver_test.go index 1d065aab2fd..1d34b5106c8 100644 --- a/pkg/tests/apis/alerting/notifications/receivers/receiver_test.go +++ b/pkg/tests/apis/alerting/notifications/receivers/receiver_test.go @@ -13,6 +13,7 @@ import ( "strings" "testing" + "github.com/grafana/alerting/receivers/schema" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "k8s.io/apimachinery/pkg/api/errors" @@ -37,7 +38,6 @@ import ( "github.com/grafana/grafana/pkg/services/ngalert/api" "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models" - "github.com/grafana/grafana/pkg/services/ngalert/notifier/channels_config" "github.com/grafana/grafana/pkg/services/ngalert/store" "github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/tests/api/alerting" @@ -1311,12 +1311,14 @@ func TestIntegrationCRUD(t *testing.T) { require.Equal(t, receiver, get) t.Run("should return secrets in secureFields but not settings", func(t *testing.T) { for _, integration := range get.Spec.Integrations { + integrationType := schema.IntegrationType(integration.Type) t.Run(integration.Type, func(t *testing.T) { expected := notify.AllKnownConfigsForTesting[strings.ToLower(integration.Type)] var fields map[string]any require.NoError(t, json.Unmarshal([]byte(expected.Config), &fields)) - secretFields, err := channels_config.GetSecretKeysForContactPointType(integration.Type, channels_config.V1) - require.NoError(t, err) + typeSchema, ok := notify.GetSchemaVersionForIntegration(integrationType, schema.V1) + require.True(t, ok) + secretFields := typeSchema.GetSecretFieldsPaths() for _, field := range secretFields { if _, ok := fields[field]; !ok { // skip field that is not in the original setting continue