mirror of
https://github.com/grafana/grafana.git
synced 2025-02-09 06:56:07 -06:00
Alerting: Export of contact points to HCL (#75849)
* add compat layer to convert from Export model to "new" API models
This commit is contained in:
parent
0a6d78f35e
commit
372082d254
@ -548,15 +548,18 @@ func exportHcl(download bool, body definitions.AlertingFileExport) response.Resp
|
||||
Body: &gr,
|
||||
})
|
||||
}
|
||||
for idx, cp := range body.ContactPoints {
|
||||
upd, err := ContactPointFromContactPointExport(cp)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "failed to convert contact points to HCL", err)
|
||||
}
|
||||
resources = append(resources, hcl.Resource{
|
||||
Type: "grafana_contact_point",
|
||||
Name: fmt.Sprintf("contact_point_%d", idx),
|
||||
Body: &upd,
|
||||
})
|
||||
}
|
||||
|
||||
// TODO implement support.
|
||||
// for idx, cp := range ex.ContactPoints {
|
||||
// resources = append(resources, resourceBlock{
|
||||
// Type: "grafana_contact_point",
|
||||
// Name: fmt.Sprintf("contact_point_%d", idx),
|
||||
// Body: &cp,
|
||||
// })
|
||||
// }
|
||||
for idx, cp := range body.Policies {
|
||||
policy := cp.Policy
|
||||
resources = append(resources, hcl.Resource{
|
||||
|
446
pkg/services/ngalert/api/compat_contact_points.go
Normal file
446
pkg/services/ngalert/api/compat_contact_points.go
Normal file
@ -0,0 +1,446 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/grafana/alerting/notify"
|
||||
"github.com/grafana/alerting/receivers"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/modern-go/reflect2"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
// ContactPointFromContactPointExport parses the database model of the contact point (group of integrations) where settings are represented in JSON,
|
||||
// to strongly typed ContactPoint.
|
||||
func ContactPointFromContactPointExport(rawContactPoint definitions.ContactPointExport) (definitions.ContactPoint, error) {
|
||||
j := jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
j.RegisterExtension(&contactPointsExtension{})
|
||||
|
||||
contactPoint := definitions.ContactPoint{
|
||||
Name: rawContactPoint.Name,
|
||||
}
|
||||
var errs []error
|
||||
for _, rawIntegration := range rawContactPoint.Receivers {
|
||||
err := parseIntegration(j, &contactPoint, rawIntegration.Type, rawIntegration.DisableResolveMessage, json.RawMessage(rawIntegration.Settings))
|
||||
if err != nil {
|
||||
// accumulate errors to report all at once.
|
||||
errs = append(errs, fmt.Errorf("failed to parse %s integration (uid:%s): %w", rawIntegration.Type, rawIntegration.UID, err))
|
||||
}
|
||||
}
|
||||
return contactPoint, errors.Join(errs...)
|
||||
}
|
||||
|
||||
// ContactPointToContactPointExport converts definitions.ContactPoint to notify.APIReceiver.
|
||||
// It uses special extension for json-iterator API that properly handles marshalling of some specific fields.
|
||||
//
|
||||
//nolint:gocyclo
|
||||
func ContactPointToContactPointExport(cp definitions.ContactPoint) (notify.APIReceiver, error) {
|
||||
j := jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
// use json iterator with custom extension that has special codec for some field.
|
||||
// This is needed to keep the API models clean and convert from database model
|
||||
j.RegisterExtension(&contactPointsExtension{})
|
||||
|
||||
var integration []*notify.GrafanaIntegrationConfig
|
||||
|
||||
var errs []error
|
||||
for _, i := range cp.Alertmanager {
|
||||
el, err := marshallIntegration(j, "prometheus-alertmanager", i, i.DisableResolveMessage)
|
||||
integration = append(integration, el)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
for _, i := range cp.Dingding {
|
||||
el, err := marshallIntegration(j, "dingding", i, i.DisableResolveMessage)
|
||||
integration = append(integration, el)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
for _, i := range cp.Discord {
|
||||
el, err := marshallIntegration(j, "discord", i, i.DisableResolveMessage)
|
||||
integration = append(integration, el)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
for _, i := range cp.Email {
|
||||
el, err := marshallIntegration(j, "email", i, i.DisableResolveMessage)
|
||||
integration = append(integration, el)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
for _, i := range cp.Googlechat {
|
||||
el, err := marshallIntegration(j, "googlechat", i, i.DisableResolveMessage)
|
||||
integration = append(integration, el)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
for _, i := range cp.Kafka {
|
||||
el, err := marshallIntegration(j, "kafka", i, i.DisableResolveMessage)
|
||||
integration = append(integration, el)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
for _, i := range cp.Line {
|
||||
el, err := marshallIntegration(j, "line", i, i.DisableResolveMessage)
|
||||
integration = append(integration, el)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
for _, i := range cp.Opsgenie {
|
||||
el, err := marshallIntegration(j, "opsgenie", i, i.DisableResolveMessage)
|
||||
integration = append(integration, el)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
for _, i := range cp.Pagerduty {
|
||||
el, err := marshallIntegration(j, "pagerduty", i, i.DisableResolveMessage)
|
||||
integration = append(integration, el)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
for _, i := range cp.OnCall {
|
||||
el, err := marshallIntegration(j, "oncall", i, i.DisableResolveMessage)
|
||||
integration = append(integration, el)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
for _, i := range cp.Pushover {
|
||||
el, err := marshallIntegration(j, "pushover", i, i.DisableResolveMessage)
|
||||
integration = append(integration, el)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
for _, i := range cp.Sensugo {
|
||||
el, err := marshallIntegration(j, "sensugo", i, i.DisableResolveMessage)
|
||||
integration = append(integration, el)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
for _, i := range cp.Slack {
|
||||
el, err := marshallIntegration(j, "slack", i, i.DisableResolveMessage)
|
||||
integration = append(integration, el)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
for _, i := range cp.Teams {
|
||||
el, err := marshallIntegration(j, "teams", i, i.DisableResolveMessage)
|
||||
integration = append(integration, el)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
for _, i := range cp.Telegram {
|
||||
el, err := marshallIntegration(j, "telegram", i, i.DisableResolveMessage)
|
||||
integration = append(integration, el)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
for _, i := range cp.Threema {
|
||||
el, err := marshallIntegration(j, "threema", i, i.DisableResolveMessage)
|
||||
integration = append(integration, el)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
for _, i := range cp.Victorops {
|
||||
el, err := marshallIntegration(j, "victorops", i, i.DisableResolveMessage)
|
||||
integration = append(integration, el)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
for _, i := range cp.Webhook {
|
||||
el, err := marshallIntegration(j, "webhook", i, i.DisableResolveMessage)
|
||||
integration = append(integration, el)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
for _, i := range cp.Wecom {
|
||||
el, err := marshallIntegration(j, "wecom", i, i.DisableResolveMessage)
|
||||
integration = append(integration, el)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
for _, i := range cp.Webex {
|
||||
el, err := marshallIntegration(j, "webex", i, i.DisableResolveMessage)
|
||||
integration = append(integration, el)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return notify.APIReceiver{}, errors.Join(errs...)
|
||||
}
|
||||
contactPoint := notify.APIReceiver{
|
||||
ConfigReceiver: notify.ConfigReceiver{Name: cp.Name},
|
||||
GrafanaIntegrations: notify.GrafanaIntegrations{Integrations: integration},
|
||||
}
|
||||
return contactPoint, nil
|
||||
}
|
||||
|
||||
// marshallIntegration converts the API model integration to the storage model that contains settings in the JSON format.
|
||||
// The secret fields are not encrypted.
|
||||
func marshallIntegration(json jsoniter.API, integrationType string, integration interface{}, disableResolveMessage *bool) (*notify.GrafanaIntegrationConfig, error) {
|
||||
data, err := json.Marshal(integration)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshall integration '%s' to JSON: %w", integrationType, err)
|
||||
}
|
||||
e := ¬ify.GrafanaIntegrationConfig{
|
||||
Type: integrationType,
|
||||
Settings: data,
|
||||
}
|
||||
if disableResolveMessage != nil {
|
||||
e.DisableResolveMessage = *disableResolveMessage
|
||||
}
|
||||
return e, nil
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
func parseIntegration(json jsoniter.API, result *definitions.ContactPoint, receiverType string, disableResolveMessage bool, data json.RawMessage) error {
|
||||
var err error
|
||||
var disable *bool
|
||||
if disableResolveMessage { // populate only if true
|
||||
disable = util.Pointer(disableResolveMessage)
|
||||
}
|
||||
switch strings.ToLower(receiverType) {
|
||||
case "prometheus-alertmanager":
|
||||
integration := definitions.AlertmanagerIntegration{DisableResolveMessage: disable}
|
||||
if err = json.Unmarshal(data, &integration); err == nil {
|
||||
result.Alertmanager = append(result.Alertmanager, integration)
|
||||
}
|
||||
case "dingding":
|
||||
integration := definitions.DingdingIntegration{DisableResolveMessage: disable}
|
||||
if err = json.Unmarshal(data, &integration); err == nil {
|
||||
result.Dingding = append(result.Dingding, integration)
|
||||
}
|
||||
case "discord":
|
||||
integration := definitions.DiscordIntegration{DisableResolveMessage: disable}
|
||||
if err = json.Unmarshal(data, &integration); err == nil {
|
||||
result.Discord = append(result.Discord, integration)
|
||||
}
|
||||
case "email":
|
||||
integration := definitions.EmailIntegration{DisableResolveMessage: disable}
|
||||
if err = json.Unmarshal(data, &integration); err == nil {
|
||||
result.Email = append(result.Email, integration)
|
||||
}
|
||||
case "googlechat":
|
||||
integration := definitions.GooglechatIntegration{DisableResolveMessage: disable}
|
||||
if err = json.Unmarshal(data, &integration); err == nil {
|
||||
result.Googlechat = append(result.Googlechat, integration)
|
||||
}
|
||||
case "kafka":
|
||||
integration := definitions.KafkaIntegration{DisableResolveMessage: disable}
|
||||
if err = json.Unmarshal(data, &integration); err == nil {
|
||||
result.Kafka = append(result.Kafka, integration)
|
||||
}
|
||||
case "line":
|
||||
integration := definitions.LineIntegration{DisableResolveMessage: disable}
|
||||
if err = json.Unmarshal(data, &integration); err == nil {
|
||||
result.Line = append(result.Line, integration)
|
||||
}
|
||||
case "opsgenie":
|
||||
integration := definitions.OpsgenieIntegration{DisableResolveMessage: disable}
|
||||
if err = json.Unmarshal(data, &integration); err == nil {
|
||||
result.Opsgenie = append(result.Opsgenie, integration)
|
||||
}
|
||||
case "pagerduty":
|
||||
integration := definitions.PagerdutyIntegration{DisableResolveMessage: disable}
|
||||
if err = json.Unmarshal(data, &integration); err == nil {
|
||||
result.Pagerduty = append(result.Pagerduty, integration)
|
||||
}
|
||||
case "oncall":
|
||||
integration := definitions.OnCallIntegration{DisableResolveMessage: disable}
|
||||
if err = json.Unmarshal(data, &integration); err == nil {
|
||||
result.OnCall = append(result.OnCall, integration)
|
||||
}
|
||||
case "pushover":
|
||||
integration := definitions.PushoverIntegration{DisableResolveMessage: disable}
|
||||
if err = json.Unmarshal(data, &integration); err == nil {
|
||||
result.Pushover = append(result.Pushover, integration)
|
||||
}
|
||||
case "sensugo":
|
||||
integration := definitions.SensugoIntegration{DisableResolveMessage: disable}
|
||||
if err = json.Unmarshal(data, &integration); err == nil {
|
||||
result.Sensugo = append(result.Sensugo, integration)
|
||||
}
|
||||
case "slack":
|
||||
integration := definitions.SlackIntegration{DisableResolveMessage: disable}
|
||||
if err = json.Unmarshal(data, &integration); err == nil {
|
||||
result.Slack = append(result.Slack, integration)
|
||||
}
|
||||
case "teams":
|
||||
integration := definitions.TeamsIntegration{DisableResolveMessage: disable}
|
||||
if err = json.Unmarshal(data, &integration); err == nil {
|
||||
result.Teams = append(result.Teams, integration)
|
||||
}
|
||||
case "telegram":
|
||||
integration := definitions.TelegramIntegration{DisableResolveMessage: disable}
|
||||
if err = json.Unmarshal(data, &integration); err == nil {
|
||||
result.Telegram = append(result.Telegram, integration)
|
||||
}
|
||||
case "threema":
|
||||
integration := definitions.ThreemaIntegration{DisableResolveMessage: disable}
|
||||
if err = json.Unmarshal(data, &integration); err == nil {
|
||||
result.Threema = append(result.Threema, integration)
|
||||
}
|
||||
case "victorops":
|
||||
integration := definitions.VictoropsIntegration{DisableResolveMessage: disable}
|
||||
if err = json.Unmarshal(data, &integration); err == nil {
|
||||
result.Victorops = append(result.Victorops, integration)
|
||||
}
|
||||
case "webhook":
|
||||
integration := definitions.WebhookIntegration{DisableResolveMessage: disable}
|
||||
if err = json.Unmarshal(data, &integration); err == nil {
|
||||
result.Webhook = append(result.Webhook, integration)
|
||||
}
|
||||
case "wecom":
|
||||
integration := definitions.WecomIntegration{DisableResolveMessage: disable}
|
||||
if err = json.Unmarshal(data, &integration); err == nil {
|
||||
result.Wecom = append(result.Wecom, integration)
|
||||
}
|
||||
case "webex":
|
||||
integration := definitions.WebexIntegration{DisableResolveMessage: disable}
|
||||
if err = json.Unmarshal(data, &integration); err == nil {
|
||||
result.Webex = append(result.Webex, integration)
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("integration %s is not supported", receiverType)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// contactPointsExtension extends jsoniter with special codecs for some integrations' fields that are encoded differently in the legacy configuration.
|
||||
type contactPointsExtension struct {
|
||||
jsoniter.DummyExtension
|
||||
}
|
||||
|
||||
func (c contactPointsExtension) UpdateStructDescriptor(structDescriptor *jsoniter.StructDescriptor) {
|
||||
if structDescriptor.Type == reflect2.TypeOf(definitions.EmailIntegration{}) {
|
||||
bind := structDescriptor.GetField("Addresses")
|
||||
codec := &emailAddressCodec{}
|
||||
bind.Decoder = codec
|
||||
bind.Encoder = codec
|
||||
}
|
||||
if structDescriptor.Type == reflect2.TypeOf(definitions.PushoverIntegration{}) {
|
||||
codec := &numberAsStringCodec{}
|
||||
for _, field := range []string{"AlertingPriority", "OKPriority"} {
|
||||
desc := structDescriptor.GetField(field)
|
||||
desc.Decoder = codec
|
||||
desc.Encoder = codec
|
||||
}
|
||||
// the same logic is in the pushover.NewConfig in alerting module
|
||||
codec = &numberAsStringCodec{ignoreError: true}
|
||||
for _, field := range []string{"Retry", "Expire"} {
|
||||
desc := structDescriptor.GetField(field)
|
||||
desc.Decoder = codec
|
||||
desc.Encoder = codec
|
||||
}
|
||||
}
|
||||
if structDescriptor.Type == reflect2.TypeOf(definitions.WebhookIntegration{}) {
|
||||
codec := &numberAsStringCodec{ignoreError: true}
|
||||
desc := structDescriptor.GetField("MaxAlerts")
|
||||
desc.Decoder = codec
|
||||
desc.Encoder = codec
|
||||
}
|
||||
if structDescriptor.Type == reflect2.TypeOf(definitions.OnCallIntegration{}) {
|
||||
codec := &numberAsStringCodec{ignoreError: true}
|
||||
desc := structDescriptor.GetField("MaxAlerts")
|
||||
desc.Decoder = codec
|
||||
desc.Encoder = codec
|
||||
}
|
||||
}
|
||||
|
||||
type emailAddressCodec struct{}
|
||||
|
||||
func (d *emailAddressCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
f := *(*[]string)(ptr)
|
||||
return len(f) == 0
|
||||
}
|
||||
|
||||
func (d *emailAddressCodec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
||||
f := *(*[]string)(ptr)
|
||||
addresses := strings.Join(f, ";")
|
||||
stream.WriteString(addresses)
|
||||
}
|
||||
|
||||
func (d *emailAddressCodec) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
s := iter.ReadString()
|
||||
emails := strings.FieldsFunc(strings.Trim(s, "\""), func(r rune) bool {
|
||||
switch r {
|
||||
case ',', ';', '\n':
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
*((*[]string)(ptr)) = emails
|
||||
}
|
||||
|
||||
// converts a string representation of a number to *int64
|
||||
type numberAsStringCodec struct {
|
||||
ignoreError bool // if true, then ignores the error and keeps value nil
|
||||
}
|
||||
|
||||
func (d *numberAsStringCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
||||
return *((*(*int))(ptr)) == nil
|
||||
}
|
||||
|
||||
func (d *numberAsStringCodec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
||||
val := *((*(*int))(ptr))
|
||||
if val == nil {
|
||||
stream.WriteNil()
|
||||
return
|
||||
}
|
||||
stream.WriteInt(*val)
|
||||
}
|
||||
|
||||
func (d *numberAsStringCodec) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||
valueType := iter.WhatIsNext()
|
||||
var value int64
|
||||
switch valueType {
|
||||
case jsoniter.NumberValue:
|
||||
value = iter.ReadInt64()
|
||||
case jsoniter.StringValue:
|
||||
var num receivers.OptionalNumber
|
||||
err := num.UnmarshalJSON(iter.ReadStringAsSlice())
|
||||
if err != nil {
|
||||
iter.ReportError("numberAsStringCodec", fmt.Sprintf("failed to unmarshall string as OptionalNumber: %s", err.Error()))
|
||||
}
|
||||
if num.String() == "" {
|
||||
return
|
||||
}
|
||||
value, err = num.Int64()
|
||||
if err != nil {
|
||||
if !d.ignoreError {
|
||||
iter.ReportError("numberAsStringCodec", fmt.Sprintf("string does not represent an integer number: %s", err.Error()))
|
||||
}
|
||||
return
|
||||
}
|
||||
case jsoniter.NilValue:
|
||||
iter.ReadNil()
|
||||
return
|
||||
default:
|
||||
iter.ReportError("numberAsStringCodec", "not number or string")
|
||||
}
|
||||
*((*(*int64))(ptr)) = &value
|
||||
}
|
187
pkg/services/ngalert/api/compat_contact_points_test.go
Normal file
187
pkg/services/ngalert/api/compat_contact_points_test.go
Normal file
@ -0,0 +1,187 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/grafana/alerting/notify"
|
||||
receiversTesting "github.com/grafana/alerting/receivers/testing"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/provisioning"
|
||||
)
|
||||
|
||||
// Test that conversion notify.APIReceiver -> definitions.ContactPoint -> notify.APIReceiver does not lose data
|
||||
func TestContactPointFromContactPointExports(t *testing.T) {
|
||||
getContactPointExport := func(t *testing.T, receiver *notify.APIReceiver) definitions.ContactPointExport {
|
||||
export := make([]definitions.ReceiverExport, 0, len(receiver.Integrations))
|
||||
for _, integrationConfig := range receiver.Integrations {
|
||||
postable := &definitions.PostableGrafanaReceiver{
|
||||
UID: integrationConfig.UID,
|
||||
Name: integrationConfig.Name,
|
||||
Type: integrationConfig.Type,
|
||||
DisableResolveMessage: integrationConfig.DisableResolveMessage,
|
||||
Settings: definitions.RawMessage(integrationConfig.Settings),
|
||||
SecureSettings: integrationConfig.SecureSettings,
|
||||
}
|
||||
emb, err := provisioning.PostableGrafanaReceiverToEmbeddedContactPoint(
|
||||
postable,
|
||||
models.ProvenanceNone,
|
||||
func(s string) string { // test configs are not encrypted but encoded
|
||||
d, err := base64.StdEncoding.DecodeString(s)
|
||||
require.NoError(t, err)
|
||||
return string(d)
|
||||
})
|
||||
require.NoError(t, err)
|
||||
ex, err := ReceiverExportFromEmbeddedContactPoint(emb)
|
||||
require.NoError(t, err)
|
||||
export = append(export, ex)
|
||||
}
|
||||
|
||||
return definitions.ContactPointExport{
|
||||
OrgID: 1,
|
||||
Name: receiver.Name,
|
||||
Receivers: export,
|
||||
}
|
||||
}
|
||||
|
||||
// use the configs for testing because they have all fields supported by integrations
|
||||
for integrationType, cfg := range notify.AllKnownConfigsForTesting {
|
||||
t.Run(integrationType, func(t *testing.T) {
|
||||
recCfg := ¬ify.APIReceiver{
|
||||
ConfigReceiver: notify.ConfigReceiver{Name: "test-receiver"},
|
||||
GrafanaIntegrations: notify.GrafanaIntegrations{
|
||||
Integrations: []*notify.GrafanaIntegrationConfig{
|
||||
cfg.GetRawNotifierConfig("test"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
expected, err := notify.BuildReceiverConfiguration(context.Background(), recCfg, func(ctx context.Context, sjd map[string][]byte, key string, fallback string) string {
|
||||
return receiversTesting.DecryptForTesting(sjd)(key, fallback)
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
result, err := ContactPointFromContactPointExport(getContactPointExport(t, recCfg))
|
||||
require.NoError(t, err)
|
||||
|
||||
back, err := ContactPointToContactPointExport(result)
|
||||
require.NoError(t, err)
|
||||
|
||||
actual, err := notify.BuildReceiverConfiguration(context.Background(), &back, func(ctx context.Context, sjd map[string][]byte, key string, fallback string) string {
|
||||
return receiversTesting.DecryptForTesting(sjd)(key, fallback)
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
diff := cmp.Diff(expected, actual, cmp.FilterPath(func(path cmp.Path) bool {
|
||||
return strings.Contains(path.String(), "Metadata.UID") ||
|
||||
strings.Contains(path.String(), "Metadata.Name") ||
|
||||
strings.Contains(path.String(), "WecomConfigs.Settings.EndpointURL") // This field is not exposed to user
|
||||
}, cmp.Ignore()))
|
||||
if len(diff) != 0 {
|
||||
require.Failf(t, "The re-marshalled configuration does not match the expected one", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("pushover optional numbers as string", func(t *testing.T) {
|
||||
export := definitions.ContactPointExport{
|
||||
Name: "test",
|
||||
Receivers: []definitions.ReceiverExport{
|
||||
{
|
||||
Type: "pushover",
|
||||
Settings: definitions.RawMessage(
|
||||
`{
|
||||
"priority": 1,
|
||||
"okPriority": "2",
|
||||
"expire": null,
|
||||
"retry": "invalid"
|
||||
}`),
|
||||
},
|
||||
},
|
||||
}
|
||||
result, err := ContactPointFromContactPointExport(export)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, result.Pushover, 1)
|
||||
require.Equal(t, int64(1), *result.Pushover[0].AlertingPriority)
|
||||
require.Equal(t, int64(2), *result.Pushover[0].OKPriority)
|
||||
require.Nil(t, result.Pushover[0].Expire)
|
||||
require.Nil(t, result.Pushover[0].Retry)
|
||||
})
|
||||
t.Run("email with multiple addresses", func(t *testing.T) {
|
||||
export := definitions.ContactPointExport{
|
||||
Name: "test",
|
||||
Receivers: []definitions.ReceiverExport{
|
||||
{
|
||||
Type: "email",
|
||||
Settings: definitions.RawMessage(`{"addresses": "test@grafana.com,test2@grafana.com;test3@grafana.com\ntest4@granafa.com"}`),
|
||||
},
|
||||
},
|
||||
}
|
||||
result, err := ContactPointFromContactPointExport(export)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, result.Email, 1)
|
||||
require.EqualValues(t, []string{
|
||||
"test@grafana.com",
|
||||
"test2@grafana.com",
|
||||
"test3@grafana.com",
|
||||
"test4@granafa.com",
|
||||
}, result.Email[0].Addresses)
|
||||
})
|
||||
t.Run("webhook with optional numbers as string", func(t *testing.T) {
|
||||
export := definitions.ContactPointExport{
|
||||
Name: "test",
|
||||
Receivers: []definitions.ReceiverExport{
|
||||
{
|
||||
Type: "webhook",
|
||||
Settings: definitions.RawMessage(`{ "maxAlerts" : "112" }`),
|
||||
},
|
||||
{
|
||||
Type: "webhook",
|
||||
Settings: definitions.RawMessage(`{ "maxAlerts" : "test" }`),
|
||||
},
|
||||
{
|
||||
Type: "webhook",
|
||||
Settings: definitions.RawMessage(`{ "maxAlerts" : null }`),
|
||||
},
|
||||
},
|
||||
}
|
||||
result, err := ContactPointFromContactPointExport(export)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, result.Webhook, 3)
|
||||
require.Equal(t, int64(112), *result.Webhook[0].MaxAlerts)
|
||||
require.Nil(t, result.Webhook[1].MaxAlerts)
|
||||
require.Nil(t, result.Webhook[2].MaxAlerts)
|
||||
})
|
||||
|
||||
t.Run("oncall with optional numbers as string", func(t *testing.T) {
|
||||
export := definitions.ContactPointExport{
|
||||
Name: "test",
|
||||
Receivers: []definitions.ReceiverExport{
|
||||
{
|
||||
Type: "oncall",
|
||||
Settings: definitions.RawMessage(`{ "maxAlerts" : "112" }`),
|
||||
},
|
||||
{
|
||||
Type: "oncall",
|
||||
Settings: definitions.RawMessage(`{ "maxAlerts" : "test" }`),
|
||||
},
|
||||
{
|
||||
Type: "oncall",
|
||||
Settings: definitions.RawMessage(`{ "maxAlerts" : null }`),
|
||||
},
|
||||
},
|
||||
}
|
||||
result, err := ContactPointFromContactPointExport(export)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, result.OnCall, 3)
|
||||
require.Equal(t, int64(112), *result.OnCall[0].MaxAlerts)
|
||||
require.Nil(t, result.OnCall[1].MaxAlerts)
|
||||
require.Nil(t, result.OnCall[2].MaxAlerts)
|
||||
})
|
||||
}
|
284
pkg/services/ngalert/api/tooling/definitions/contact_points.go
Normal file
284
pkg/services/ngalert/api/tooling/definitions/contact_points.go
Normal file
@ -0,0 +1,284 @@
|
||||
package definitions
|
||||
|
||||
// This file contains API models of integrations that are supported by Grafana Managed Alerts.
|
||||
// The models below match the Config models described in the module github.com/grafana/alerting, package 'receivers/**'
|
||||
// as well as models described in Grafana Terraform Provider.
|
||||
// Currently, they are used only for export to HCL but in the future we expand their scope.
|
||||
// The consistency between models in the alerting module and this file is enforced by unit-tests.
|
||||
|
||||
//
|
||||
// 1. JSON tags are used for unmarshalling from the definitions.PostableGrafanaReceiver.Settings.
|
||||
// 2. YAML tags are not used but kept while copying of models from the alerting module
|
||||
// 3. Each integration struct contains field 'DisableResolveMessage'. In Terraform provider the field is on the same level as the settings.
|
||||
// Currently, HCL encoder does not support composition of structures or generic ones. This can be change after https://github.com/hashicorp/hcl/issues/290 is solved.
|
||||
// 4. Sensitive fields have type Secret. Currently, this is done for information purpose and is not used anywhere.
|
||||
|
||||
// A string that contain sensitive information.
|
||||
type Secret string // TODO implement masking fields when models are used
|
||||
|
||||
type AlertmanagerIntegration struct {
|
||||
DisableResolveMessage *bool `json:"-" yaml:"-" hcl:"disable_resolve_message"` // TODO change when https://github.com/hashicorp/hcl/issues/290 is fixed
|
||||
|
||||
URL string `json:"url" yaml:"url" hcl:"url"`
|
||||
User *string `json:"basicAuthUser,omitempty" yaml:"basicAuthUser,omitempty" hcl:"basic_auth_user"`
|
||||
Password *Secret `json:"basicAuthPassword,omitempty" yaml:"basicAuthPassword,omitempty" hcl:"basic_auth_password"`
|
||||
}
|
||||
|
||||
type DingdingIntegration struct {
|
||||
DisableResolveMessage *bool `json:"-" yaml:"-" hcl:"disable_resolve_message"`
|
||||
|
||||
URL string `json:"url,omitempty" yaml:"url,omitempty" hcl:"url"`
|
||||
MessageType *string `json:"msgType,omitempty" yaml:"msgType,omitempty" hcl:"message_type"`
|
||||
Title *string `json:"title,omitempty" yaml:"title,omitempty" hcl:"title"`
|
||||
Message *string `json:"message,omitempty" yaml:"message,omitempty" hcl:"message"`
|
||||
}
|
||||
|
||||
type DiscordIntegration struct {
|
||||
DisableResolveMessage *bool `json:"-" yaml:"-" hcl:"disable_resolve_message"`
|
||||
|
||||
WebhookURL Secret `json:"url" yaml:"url" hcl:"url"`
|
||||
Title *string `json:"title,omitempty" yaml:"title,omitempty" hcl:"title"`
|
||||
Message *string `json:"message,omitempty" yaml:"message,omitempty" hcl:"message"`
|
||||
AvatarURL *string `json:"avatar_url,omitempty" yaml:"avatar_url,omitempty" hcl:"avatar_url"`
|
||||
UseDiscordUsername *bool `json:"use_discord_username,omitempty" yaml:"use_discord_username,omitempty" hcl:"use_discord_username"`
|
||||
}
|
||||
|
||||
type EmailIntegration struct {
|
||||
DisableResolveMessage *bool `json:"-" yaml:"-" hcl:"disable_resolve_message"`
|
||||
|
||||
Addresses []string `json:"addresses" yaml:"addresses" hcl:"addresses"`
|
||||
|
||||
SingleEmail *bool `json:"singleEmail,omitempty" yaml:"singleEmail,omitempty" hcl:"single_email"`
|
||||
Message *string `json:"message,omitempty" yaml:"message,omitempty" hcl:"message"`
|
||||
Subject *string `json:"subject,omitempty" yaml:"subject,omitempty" hcl:"subject"`
|
||||
}
|
||||
|
||||
type GooglechatIntegration struct {
|
||||
DisableResolveMessage *bool `json:"-" yaml:"-" hcl:"disable_resolve_message"`
|
||||
|
||||
URL string `json:"url" yaml:"url" hcl:"url"`
|
||||
|
||||
Title *string `json:"title,omitempty" yaml:"title,omitempty" hcl:"title"`
|
||||
Message *string `json:"message,omitempty" yaml:"message,omitempty" hcl:"message"`
|
||||
}
|
||||
|
||||
type KafkaIntegration struct {
|
||||
DisableResolveMessage *bool `json:"-" yaml:"-" hcl:"disable_resolve_message"`
|
||||
|
||||
Endpoint Secret `json:"kafkaRestProxy" yaml:"kafkaRestProxy" hcl:"rest_proxy_url"`
|
||||
Topic string `json:"kafkaTopic" yaml:"kafkaTopic" hcl:"topic"`
|
||||
|
||||
Description *string `json:"description,omitempty" yaml:"description,omitempty" hcl:"description"`
|
||||
Details *string `json:"details,omitempty" yaml:"details,omitempty" hcl:"details"`
|
||||
Username *string `json:"username,omitempty" yaml:"username,omitempty" hcl:"username"`
|
||||
Password *Secret `json:"password,omitempty" yaml:"password,omitempty" hcl:"password"`
|
||||
APIVersion *string `json:"apiVersion,omitempty" yaml:"apiVersion,omitempty" hcl:"api_version"`
|
||||
KafkaClusterID *string `json:"kafkaClusterId,omitempty" yaml:"kafkaClusterId,omitempty" hcl:"cluster_id"`
|
||||
}
|
||||
|
||||
type LineIntegration struct {
|
||||
DisableResolveMessage *bool `json:"-" yaml:"-" hcl:"disable_resolve_message"`
|
||||
|
||||
Token Secret `json:"token" yaml:"token" hcl:"token"`
|
||||
|
||||
Title *string `json:"title,omitempty" yaml:"title,omitempty" hcl:"title"`
|
||||
Description *string `json:"description,omitempty" yaml:"description,omitempty" hcl:"description"`
|
||||
}
|
||||
|
||||
type OnCallIntegration struct {
|
||||
DisableResolveMessage *bool `json:"-" yaml:"-" hcl:"disable_resolve_message"`
|
||||
|
||||
URL string `json:"url" yaml:"url" hcl:"url"`
|
||||
|
||||
HTTPMethod *string `json:"httpMethod,omitempty" yaml:"httpMethod,omitempty" hcl:"http_method"`
|
||||
MaxAlerts *int64 `json:"maxAlerts,omitempty" yaml:"maxAlerts,omitempty" hcl:"max_alerts"`
|
||||
AuthorizationScheme *string `json:"authorization_scheme,omitempty" yaml:"authorization_scheme,omitempty" hcl:"authorization_scheme"`
|
||||
AuthorizationCredentials *Secret `json:"authorization_credentials,omitempty" yaml:"authorization_credentials,omitempty" hcl:"authorization_credentials"`
|
||||
User *string `json:"username,omitempty" yaml:"username,omitempty" hcl:"basic_auth_user"`
|
||||
Password *Secret `json:"password,omitempty" yaml:"password,omitempty" hcl:"basic_auth_password"`
|
||||
Title *string `json:"title,omitempty" yaml:"title,omitempty" hcl:"title"`
|
||||
Message *string `json:"message,omitempty" yaml:"message,omitempty" hcl:"message"`
|
||||
}
|
||||
|
||||
type OpsgenieIntegration struct {
|
||||
DisableResolveMessage *bool `json:"-" yaml:"-" hcl:"disable_resolve_message"`
|
||||
|
||||
APIKey Secret `json:"apiKey" yaml:"apiKey" hcl:"api_key"`
|
||||
|
||||
APIUrl *string `json:"apiUrl,omitempty" yaml:"apiUrl,omitempty" hcl:"url"`
|
||||
Message *string `json:"message,omitempty" yaml:"message,omitempty" hcl:"message"`
|
||||
Description *string `json:"description,omitempty" yaml:"description,omitempty" hcl:"description"`
|
||||
AutoClose *bool `json:"autoClose,omitempty" yaml:"autoClose,omitempty" hcl:"auto_close"`
|
||||
OverridePriority *bool `json:"overridePriority,omitempty" yaml:"overridePriority,omitempty" hcl:"override_priority"`
|
||||
SendTagsAs *string `json:"sendTagsAs,omitempty" yaml:"sendTagsAs,omitempty" hcl:"send_tags_as"`
|
||||
}
|
||||
|
||||
type PagerdutyIntegration struct {
|
||||
DisableResolveMessage *bool `json:"-" yaml:"-" hcl:"disable_resolve_message"`
|
||||
|
||||
Key Secret `json:"integrationKey" yaml:"integrationKey" hcl:"integration_key"`
|
||||
|
||||
Severity *string `json:"severity,omitempty" yaml:"severity,omitempty" hcl:"severity"`
|
||||
Class *string `json:"class,omitempty" yaml:"class,omitempty" hcl:"class"`
|
||||
Component *string `json:"component,omitempty" yaml:"component,omitempty" hcl:"component"`
|
||||
Group *string `json:"group,omitempty" yaml:"group,omitempty" hcl:"group"`
|
||||
Summary *string `json:"summary,omitempty" yaml:"summary,omitempty" hcl:"summary"`
|
||||
Source *string `json:"source,omitempty" yaml:"source,omitempty" hcl:"source"`
|
||||
Client *string `json:"client,omitempty" yaml:"client,omitempty" hcl:"client"`
|
||||
ClientURL *string `json:"client_url,omitempty" yaml:"client_url,omitempty" hcl:"client_url"`
|
||||
Details *map[string]string `json:"details,omitempty" yaml:"details,omitempty" hcl:"details"`
|
||||
}
|
||||
|
||||
type PushoverIntegration struct {
|
||||
DisableResolveMessage *bool `json:"-" yaml:"-" hcl:"disable_resolve_message"`
|
||||
|
||||
UserKey Secret `json:"userKey" yaml:"userKey" hcl:"user_key"`
|
||||
APIToken Secret `json:"apiToken" yaml:"apiToken" hcl:"api_token"`
|
||||
|
||||
AlertingPriority *int64 `json:"priority,omitempty" yaml:"priority,omitempty" hcl:"priority"`
|
||||
OKPriority *int64 `json:"okPriority,omitempty" yaml:"okPriority,omitempty" hcl:"ok_priority"`
|
||||
Retry *int64 `json:"retry,omitempty" yaml:"retry,omitempty" hcl:"retry"`
|
||||
Expire *int64 `json:"expire,omitempty" yaml:"expire,omitempty" hcl:"expire"`
|
||||
Device *string `json:"device,omitempty" yaml:"device,omitempty" hcl:"device"`
|
||||
AlertingSound *string `json:"sound,omitempty" yaml:"sound,omitempty" hcl:"sound"`
|
||||
OKSound *string `json:"okSound,omitempty" yaml:"okSound,omitempty" hcl:"ok_sound"`
|
||||
Title *string `json:"title,omitempty" yaml:"title,omitempty" hcl:"title"`
|
||||
Message *string `json:"message,omitempty" yaml:"message,omitempty" hcl:"message"`
|
||||
UploadImage *bool `json:"uploadImage,omitempty" yaml:"uploadImage,omitempty" hcl:"upload_image"`
|
||||
}
|
||||
|
||||
type SensugoIntegration struct {
|
||||
DisableResolveMessage *bool `json:"-" yaml:"-" hcl:"disable_resolve_message"`
|
||||
|
||||
URL string `json:"url" yaml:"url" hcl:"url"`
|
||||
APIKey Secret `json:"apikey" yaml:"apikey" hcl:"api_key"`
|
||||
|
||||
Entity *string `json:"entity,omitempty" yaml:"entity,omitempty" hcl:"entity"`
|
||||
Check *string `json:"check,omitempty" yaml:"check,omitempty" hcl:"check"`
|
||||
Namespace *string `json:"namespace,omitempty" yaml:"namespace,omitempty" hcl:"namespace"`
|
||||
Handler *string `json:"handler,omitempty" yaml:"handler,omitempty" hcl:"handler"`
|
||||
Message *string `json:"message,omitempty" yaml:"message,omitempty" hcl:"message"`
|
||||
}
|
||||
|
||||
type SlackIntegration struct {
|
||||
DisableResolveMessage *bool `json:"-" yaml:"-" hcl:"disable_resolve_message"`
|
||||
|
||||
EndpointURL *string `json:"endpointUrl,omitempty" yaml:"endpointUrl,omitempty" hcl:"endpoint_url"`
|
||||
URL *Secret `json:"url,omitempty" yaml:"url,omitempty" hcl:"url"`
|
||||
Token *Secret `json:"token,omitempty" yaml:"token,omitempty" hcl:"token"`
|
||||
Recipient *string `json:"recipient,omitempty" yaml:"recipient,omitempty" hcl:"recipient"`
|
||||
Text *string `json:"text,omitempty" yaml:"text,omitempty" hcl:"text"`
|
||||
Title *string `json:"title,omitempty" yaml:"title,omitempty" hcl:"title"`
|
||||
Username *string `json:"username,omitempty" yaml:"username,omitempty" hcl:"username"`
|
||||
IconEmoji *string `json:"icon_emoji,omitempty" yaml:"icon_emoji,omitempty" hcl:"icon_emoji"`
|
||||
IconURL *string `json:"icon_url,omitempty" yaml:"icon_url,omitempty" hcl:"icon_url"`
|
||||
MentionChannel *string `json:"mentionChannel,omitempty" yaml:"mentionChannel,omitempty" hcl:"mention_channel"`
|
||||
MentionUsers *string `json:"mentionUsers,omitempty" yaml:"mentionUsers,omitempty" hcl:"mention_users"`
|
||||
MentionGroups *string `json:"mentionGroups,omitempty" yaml:"mentionGroups,omitempty" hcl:"mention_groups"`
|
||||
}
|
||||
|
||||
type TelegramIntegration struct {
|
||||
DisableResolveMessage *bool `json:"-" yaml:"-" hcl:"disable_resolve_message"`
|
||||
|
||||
BotToken Secret `json:"bottoken" yaml:"bottoken" hcl:"token"`
|
||||
ChatID string `json:"chatid,omitempty" yaml:"chatid,omitempty" hcl:"chat_id"`
|
||||
|
||||
Message *string `json:"message,omitempty" yaml:"message,omitempty" hcl:"message"`
|
||||
ParseMode *string `json:"parse_mode,omitempty" yaml:"parse_mode,omitempty" hcl:"parse_mode"`
|
||||
DisableWebPagePreview *bool `json:"disable_web_page_preview,omitempty" yaml:"disable_web_page_preview,omitempty" hcl:"disable_web_page_preview"`
|
||||
ProtectContent *bool `json:"protect_content,omitempty" yaml:"protect_content,omitempty" hcl:"protect_content"`
|
||||
DisableNotifications *bool `json:"disable_notifications,omitempty" yaml:"disable_notifications,omitempty" hcl:"disable_notifications"`
|
||||
}
|
||||
|
||||
type TeamsIntegration struct {
|
||||
DisableResolveMessage *bool `json:"-" yaml:"-" hcl:"disable_resolve_message"`
|
||||
|
||||
URL Secret `json:"url,omitempty" yaml:"url,omitempty" hcl:"url"`
|
||||
|
||||
Message *string `json:"message,omitempty" yaml:"message,omitempty" hcl:"message"`
|
||||
Title *string `json:"title,omitempty" yaml:"title,omitempty" hcl:"title"`
|
||||
SectionTitle *string `json:"sectiontitle,omitempty" yaml:"sectiontitle,omitempty" hcl:"section_title"`
|
||||
}
|
||||
|
||||
type ThreemaIntegration struct {
|
||||
DisableResolveMessage *bool `json:"-" yaml:"-" hcl:"disable_resolve_message"`
|
||||
|
||||
GatewayID string `json:"gateway_id" yaml:"gateway_id" hcl:"gateway_id"`
|
||||
RecipientID string `json:"recipient_id" yaml:"recipient_id" hcl:"recipient_id"`
|
||||
APISecret Secret `json:"api_secret" yaml:"api_secret" hcl:"api_secret"`
|
||||
|
||||
Title *string `json:"title,omitempty" yaml:"title,omitempty" hcl:"title"`
|
||||
Description *string `json:"description,omitempty" yaml:"description,omitempty" hcl:"description"`
|
||||
}
|
||||
|
||||
type VictoropsIntegration struct {
|
||||
DisableResolveMessage *bool `json:"-" yaml:"-" hcl:"disable_resolve_message"`
|
||||
|
||||
URL string `json:"url" yaml:"url" hcl:"url"`
|
||||
|
||||
MessageType *string `json:"messageType,omitempty" yaml:"messageType,omitempty" hcl:"message_type"`
|
||||
Title *string `json:"title,omitempty" yaml:"title,omitempty" hcl:"title"`
|
||||
Description *string `json:"description,omitempty" yaml:"description,omitempty" hcl:"description"`
|
||||
}
|
||||
|
||||
type WebexIntegration struct {
|
||||
DisableResolveMessage *bool `json:"-" yaml:"-" hcl:"disable_resolve_message"`
|
||||
|
||||
Token Secret `json:"bot_token" yaml:"bot_token" hcl:"token"`
|
||||
|
||||
APIURL *string `json:"api_url,omitempty" yaml:"api_url,omitempty" hcl:"api_url"`
|
||||
Message *string `json:"message,omitempty" yaml:"message,omitempty" hcl:"message"`
|
||||
RoomID *string `json:"room_id,omitempty" yaml:"room_id,omitempty" hcl:"room_id"`
|
||||
}
|
||||
|
||||
type WebhookIntegration struct {
|
||||
DisableResolveMessage *bool `json:"-" yaml:"-" hcl:"disable_resolve_message"`
|
||||
|
||||
URL string `json:"url" yaml:"url" hcl:"url"`
|
||||
|
||||
HTTPMethod *string `json:"httpMethod,omitempty" yaml:"httpMethod,omitempty" hcl:"http_method"`
|
||||
MaxAlerts *int64 `json:"maxAlerts,omitempty" yaml:"maxAlerts,omitempty" hcl:"max_alerts"`
|
||||
AuthorizationScheme *string `json:"authorization_scheme,omitempty" yaml:"authorization_scheme,omitempty" hcl:"authorization_scheme"`
|
||||
AuthorizationCredentials *Secret `json:"authorization_credentials,omitempty" yaml:"authorization_credentials,omitempty" hcl:"authorization_credentials"`
|
||||
User *string `json:"username,omitempty" yaml:"username,omitempty" hcl:"basic_auth_user"`
|
||||
Password *Secret `json:"password,omitempty" yaml:"password,omitempty" hcl:"basic_auth_password"`
|
||||
Title *string `json:"title,omitempty" yaml:"title,omitempty" hcl:"title"`
|
||||
Message *string `json:"message,omitempty" yaml:"message,omitempty" hcl:"message"`
|
||||
}
|
||||
|
||||
type WecomIntegration struct {
|
||||
DisableResolveMessage *bool `json:"-" yaml:"-" hcl:"disable_resolve_message"`
|
||||
|
||||
URL *Secret `json:"url,omitempty" yaml:"url,omitempty" hcl:"url"`
|
||||
Secret *Secret `json:"secret,omitempty" yaml:"secret,omitempty" hcl:"secret"`
|
||||
AgentID *string `json:"agent_id,omitempty" yaml:"agent_id,omitempty" hcl:"agent_id"`
|
||||
CorpID *string `json:"corp_id,omitempty" yaml:"corp_id,omitempty" hcl:"corp_id"`
|
||||
Message *string `json:"message,omitempty" yaml:"message,omitempty" hcl:"message"`
|
||||
Title *string `json:"title,omitempty" yaml:"title,omitempty" hcl:"title"`
|
||||
MsgType *string `json:"msgtype,omitempty" yaml:"msgtype,omitempty" hcl:"msg_type"`
|
||||
ToUser *string `json:"touser,omitempty" yaml:"touser,omitempty" hcl:"to_user"`
|
||||
}
|
||||
|
||||
type ContactPoint struct {
|
||||
Name string `json:"name" yaml:"name" hcl:"name"`
|
||||
Alertmanager []AlertmanagerIntegration `json:"alertmanager" yaml:"alertmanager" hcl:"alertmanager,block"`
|
||||
Dingding []DingdingIntegration `json:"dingding" yaml:"dingding" hcl:"dingding,block"`
|
||||
Discord []DiscordIntegration `json:"discord" yaml:"discord" hcl:"discord,block"`
|
||||
Email []EmailIntegration `json:"email" yaml:"email" hcl:"email,block"`
|
||||
Googlechat []GooglechatIntegration `json:"googlechat" yaml:"googlechat" hcl:"googlechat,block"`
|
||||
Kafka []KafkaIntegration `json:"kafka" yaml:"kafka" hcl:"kafka,block"`
|
||||
Line []LineIntegration `json:"line" yaml:"line" hcl:"line,block"`
|
||||
Opsgenie []OpsgenieIntegration `json:"opsgenie" yaml:"opsgenie" hcl:"opsgenie,block"`
|
||||
Pagerduty []PagerdutyIntegration `json:"pagerduty" yaml:"pagerduty" hcl:"pagerduty,block"`
|
||||
OnCall []OnCallIntegration `json:"oncall" yaml:"oncall" hcl:"oncall,block"`
|
||||
Pushover []PushoverIntegration `json:"pushover" yaml:"pushover" hcl:"pushover,block"`
|
||||
Sensugo []SensugoIntegration `json:"sensugo" yaml:"sensugo" hcl:"sensugo,block"`
|
||||
Slack []SlackIntegration `json:"slack" yaml:"slack" hcl:"slack,block"`
|
||||
Teams []TeamsIntegration `json:"teams" yaml:"teams" hcl:"teams,block"`
|
||||
Telegram []TelegramIntegration `json:"telegram" yaml:"telegram" hcl:"telegram,block"`
|
||||
Threema []ThreemaIntegration `json:"threema" yaml:"threema" hcl:"threema,block"`
|
||||
Victorops []VictoropsIntegration `json:"victorops" yaml:"victorops" hcl:"victorops,block"`
|
||||
Webhook []WebhookIntegration `json:"webhook" yaml:"webhook" hcl:"webhook,block"`
|
||||
Wecom []WecomIntegration `json:"wecom" yaml:"wecom" hcl:"wecom,block"`
|
||||
Webex []WebexIntegration `json:"webex" yaml:"webex" hcl:"webex,block"`
|
||||
}
|
@ -6,7 +6,7 @@ import { alertRuleApi } from '../../api/alertRuleApi';
|
||||
|
||||
import { FileExportPreview } from './FileExportPreview';
|
||||
import { GrafanaExportDrawer } from './GrafanaExportDrawer';
|
||||
import { ExportFormats, jsonAndYamlGrafanaExportProviders } from './providers';
|
||||
import { allGrafanaExportProviders, ExportFormats } from './providers';
|
||||
|
||||
interface GrafanaReceiverExportPreviewProps {
|
||||
exportFormat: ExportFormats;
|
||||
@ -57,7 +57,7 @@ export const GrafanaReceiverExporter = ({ onClose, receiverName, decrypt }: Graf
|
||||
activeTab={activeTab}
|
||||
onTabChange={setActiveTab}
|
||||
onClose={onClose}
|
||||
formatProviders={jsonAndYamlGrafanaExportProviders}
|
||||
formatProviders={Object.values(allGrafanaExportProviders)}
|
||||
>
|
||||
<GrafanaReceiverExportPreview
|
||||
receiverName={receiverName}
|
||||
|
@ -6,7 +6,7 @@ import { alertRuleApi } from '../../api/alertRuleApi';
|
||||
|
||||
import { FileExportPreview } from './FileExportPreview';
|
||||
import { GrafanaExportDrawer } from './GrafanaExportDrawer';
|
||||
import { ExportFormats, jsonAndYamlGrafanaExportProviders } from './providers';
|
||||
import { allGrafanaExportProviders, ExportFormats } from './providers';
|
||||
|
||||
interface GrafanaReceiversExportPreviewProps {
|
||||
exportFormat: ExportFormats;
|
||||
@ -49,7 +49,7 @@ export const GrafanaReceiversExporter = ({ onClose, decrypt }: GrafanaReceiversE
|
||||
activeTab={activeTab}
|
||||
onTabChange={setActiveTab}
|
||||
onClose={onClose}
|
||||
formatProviders={jsonAndYamlGrafanaExportProviders}
|
||||
formatProviders={Object.values(allGrafanaExportProviders)}
|
||||
>
|
||||
<GrafanaReceiversExportPreview decrypt={decrypt} exportFormat={activeTab} onClose={onClose} />
|
||||
</GrafanaExportDrawer>
|
||||
|
Loading…
Reference in New Issue
Block a user