From 22ccfe0caf45685df4b53900b5896eb437d8b19e Mon Sep 17 00:00:00 2001 From: SLASHLogin Date: Sat, 5 Oct 2024 20:25:03 +0200 Subject: [PATCH] Add missing fields for Opsgenie integration (#339) Signed-off-by: SLASHLogin --- docs/services/opsgenie.md | 74 +++++++---- pkg/services/opsgenie.go | 232 ++++++++++++++++++++++++++++++++-- pkg/services/opsgenie_test.go | 205 ++++++++++++++++++++++++++---- 3 files changed, 452 insertions(+), 59 deletions(-) diff --git a/docs/services/opsgenie.md b/docs/services/opsgenie.md index 2cc1ebff..d0caf71c 100644 --- a/docs/services/opsgenie.md +++ b/docs/services/opsgenie.md @@ -2,28 +2,34 @@ To be able to send notifications with argocd-notifications you have to create an [API Integration](https://docs.opsgenie.com/docs/integrations-overview) inside your [Opsgenie Team](https://docs.opsgenie.com/docs/teams). -1. Login to Opsgenie at https://app.opsgenie.com or https://app.eu.opsgenie.com (if you have an account in the european union) -2. Make sure you already have a team, if not follow this guide https://docs.opsgenie.com/docs/teams -3. Click "Teams" in the Menu on the left -4. Select the team that you want to notify -5. In the teams configuration menu select "Integrations" -6. Click "Add Integration" in the top right corner -7. Select "API" integration -8. Give your integration a name, copy the "API key" and safe it somewhere for later -9. Click "Edit" in the integration settings -10. Make sure the checkbox for "Create and Update Access" is selected, disable the other checkboxes to remove unnecessary permissions -11. Click "Save" at the bottom -12. Click "Turn on integration" in the top right corner -13. Check your browser for the correct server apiURL. If it is "app.opsgenie.com" then use the US/international api url `api.opsgenie.com` in the next step, otherwise use `api.eu.opsgenie.com` (European API). -14. You are finished with configuring Opsgenie. Now you need to configure argocd-notifications. Use the apiUrl, the team name and the apiKey to configure the Opsgenie integration in the `argocd-notifications-secret` secret. -15. You can find the example `argocd-notifications-cm` configuration at the below. +1. Login to Opsgenie at https://app.opsgenie.com or https://app.eu.opsgenie.com (if you have an account in the European Union). +2. Make sure you already have a team; if not, follow this guide: https://docs.opsgenie.com/docs/teams. +3. Click "Teams" in the Menu on the left. +4. Select the team that you want to notify. +5. In the team's configuration menu, select "Integrations". +6. Click "Add Integration" in the top right corner. +7. Select "API" integration. +8. Give your integration a name, copy the "API key", and save it somewhere for later. +9. Click "Edit" in the integration settings. +10. Make sure the checkbox for "Create and Update Access" is selected; disable the other checkboxes to remove unnecessary permissions. +11. Click "Save" at the bottom. +12. Click "Turn on integration" in the top right corner. +13. Check your browser for the correct server apiURL. If it is "app.opsgenie.com", then use the US/international API URL `api.opsgenie.com`; otherwise, use `api.eu.opsgenie.com` (European API). +14. You are finished with configuring Opsgenie. Now you need to configure argocd-notifications. Use the apiUrl, the team name, and the apiKey to configure the Opsgenie integration in the `argocd-notifications-secret` secret. +15. You can find the example `argocd-notifications-cm` configuration below. | **Option** | **Required** | **Type** | **Description** | **Example** | | ------------- | ------------ | -------- | -------------------------------------------------------------------------------------------------------- | -------------------------------- | -| `description` | True | `string` | Description field of the alert that is generally used to provide a detailed information about the alert. | `Hello from Argo CD!` | -| `priority` | False | `string` | Priority level of the alert. Possible values are P1, P2, P3, P4 and P5. Default value is P3. | `P1` | +| `description` | True | `string` | Description field of the alert that is generally used to provide detailed information about the alert. | `Hello from Argo CD!` | +| `priority` | False | `string` | Priority level of the alert. Possible values are P1, P2, P3, P4, and P5. Default value is P3. | `P1` | | `alias` | False | `string` | Client-defined identifier of the alert, that is also the key element of Alert De-Duplication. | `Life is too short for no alias` | -| `note` | False | `string` | Additional note that will be added while creating the alert. | `Error from Argo CD!` | +| `note` | False | `string` | Additional note that will be added while creating the alert. | `Error from Argo CD!` | +| `actions` | False | `[]string` | Custom actions that will be available for the alert. | `["Resolve", "Escalate"]` | +| `tags` | False | `[]string` | Tags of the alert. | `["critical", "deployment"]` | +| `visibleTo` | False | `[]alert.Responder` | Teams and users that the alert will become visible to without sending any notification. The `type` field is mandatory for each item, where possible values are `team` and `user`. In addition to the `type` field, either `id` or `name` should be provided for teams, and either `id` or `username` should be given for users. Please note that alerts will be visible to the teams specified within the `responders` field by default, so there is no need to re-specify them in the `visibleTo` field. | `[{Type: "team", Id: "team_id"}, {Type: "user", Id: "user_id"}]` | +| `details` | False | `map[string]string` | Map of key-value pairs to use as custom properties of the alert. | `{"environment": "production", "service": "web"}` | +| `entity` | False | `string` | Entity field of the alert that is generally used to specify which domain the alert is related to. | `web-server` | +| `user` | False | `string` | Display name of the request owner. | `admin_user` | ```yaml apiVersion: v1 @@ -47,6 +53,26 @@ data: priority: P1 alias: {{.app.metadata.name}} note: Error from Argo CD! + actions: + - Restart + - AnExampleAction + tags: + - OverwriteQuietHours + - Critical + visibleTo: + - Id: "{{.app.metadata.responderId}}" + Type: "team" + - Name: "rocket_team" + Type: "team" + - Id: "{{.app.metadata.responderUserId}}" + Type: "user" + - Username: "trinity@opsgenie.com" + Type: "user" + details: + environment: production + service: web + entity: Argo CD Application + user: John Doe trigger.on-a-problem: | - description: Application has a problem. send: @@ -54,11 +80,11 @@ data: when: app.status.health.status == 'Degraded' or app.status.operationState.phase in ['Error', 'Failed'] or app.status.sync.status == 'Unknown' ``` -16. Add annotation in application yaml file to enable notifications for specific Argo CD app. +16. Add annotation in the application YAML file to enable notifications for a specific Argo CD app. ```yaml - apiVersion: argoproj.io/v1alpha1 - kind: Application - metadata: - annotations: - notifications.argoproj.io/subscribe.on-a-problem.opsgenie: +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + annotations: + notifications.argoproj.io/subscribe.on-a-problem.opsgenie: ``` \ No newline at end of file diff --git a/pkg/services/opsgenie.go b/pkg/services/opsgenie.go index e46b4b73..5c238581 100644 --- a/pkg/services/opsgenie.go +++ b/pkg/services/opsgenie.go @@ -20,10 +20,16 @@ type OpsgenieOptions struct { } type OpsgenieNotification struct { - Description string `json:"description"` - Priority string `json:"priority,omitempty"` - Alias string `json:"alias,omitempty"` - Note string `json:"note,omitempty"` + Description string `json:"description"` + Priority string `json:"priority,omitempty"` + Alias string `json:"alias,omitempty"` + Note string `json:"note,omitempty"` + Actions []string `json:"actions,omitempty"` + Tags []string `json:"tags,omitempty"` + Details map[string]string `json:"details,omitempty"` + Entity string `json:"entity,omitempty"` + VisibleTo []alert.Responder `json:"visibleTo,omitempty"` + User string `json:"user,omitempty"` } func (n *OpsgenieNotification) GetTemplater(name string, f texttemplate.FuncMap) (Templater, error) { @@ -31,6 +37,10 @@ func (n *OpsgenieNotification) GetTemplater(name string, f texttemplate.FuncMap) if err != nil { return nil, err } + priority, err := texttemplate.New(name).Funcs(f).Parse(n.Priority) + if err != nil { + return nil, err + } alias, err := texttemplate.New(name).Funcs(f).Parse(n.Alias) if err != nil { return nil, err @@ -39,25 +49,189 @@ func (n *OpsgenieNotification) GetTemplater(name string, f texttemplate.FuncMap) if err != nil { return nil, err } + entity, err := texttemplate.New(name).Funcs(f).Parse(n.Entity) + if err != nil { + return nil, err + } + user, err := texttemplate.New(name).Funcs(f).Parse(n.User) + if err != nil { + return nil, err + } + + details := make(map[string]*texttemplate.Template) + for key, value := range n.Details { + detailTemplate, err := texttemplate.New(fmt.Sprintf("%s_detail_%s", name, key)).Funcs(f).Parse(value) + if err != nil { + return nil, err + } + details[key] = detailTemplate + } + + visibleTo := make([]struct { + Type *texttemplate.Template + Name *texttemplate.Template + Id *texttemplate.Template + Username *texttemplate.Template + }, len(n.VisibleTo)) + + // Templating for VisibleTo Responder fields (Id, Type, Name, Username) + for i, responder := range n.VisibleTo { + idTemplate, err := texttemplate.New(fmt.Sprintf("%s_responder_%d_id", name, i)).Funcs(f).Parse(responder.Id) + if err != nil { + return nil, err + } + typeTemplate, err := texttemplate.New(fmt.Sprintf("%s_responder_%d_type", name, i)).Funcs(f).Parse(string(responder.Type)) + if err != nil { + return nil, err + } + nameTemplate, err := texttemplate.New(fmt.Sprintf("%s_responder_%d_name", name, i)).Funcs(f).Parse(responder.Name) + if err != nil { + return nil, err + } + usernameTemplate, err := texttemplate.New(fmt.Sprintf("%s_responder_%d_username", name, i)).Funcs(f).Parse(responder.Username) + if err != nil { + return nil, err + } + + visibleTo[i] = struct { + Type *texttemplate.Template + Name *texttemplate.Template + Id *texttemplate.Template + Username *texttemplate.Template + }{ + Type: typeTemplate, + Name: nameTemplate, + Id: idTemplate, + Username: usernameTemplate, + } + } + + var actionsTemplates []*texttemplate.Template + if n.Actions != nil { + actionsTemplates = make([]*texttemplate.Template, len(n.Actions)) + for i, action := range n.Actions { + actionTemplate, err := texttemplate.New(fmt.Sprintf("%s_action_%d", name, i)).Funcs(f).Parse(action) + if err != nil { + return nil, err + } + actionsTemplates[i] = actionTemplate + } + } + + var tagsTemplates []*texttemplate.Template + if n.Tags != nil { + tagsTemplates = make([]*texttemplate.Template, len(n.Tags)) + for i, tag := range n.Tags { + tagTemplate, err := texttemplate.New(fmt.Sprintf("%s_tag_%d", name, i)).Funcs(f).Parse(tag) + if err != nil { + return nil, err + } + tagsTemplates[i] = tagTemplate + } + } + return func(notification *Notification, vars map[string]interface{}) error { if notification.Opsgenie == nil { notification.Opsgenie = &OpsgenieNotification{} } + var descData bytes.Buffer if err := desc.Execute(&descData, vars); err != nil { return err } notification.Opsgenie.Description = descData.String() + var aliasData bytes.Buffer if err := alias.Execute(&aliasData, vars); err != nil { return err } notification.Opsgenie.Alias = aliasData.String() + var noteData bytes.Buffer if err := note.Execute(¬eData, vars); err != nil { return err } notification.Opsgenie.Note = noteData.String() + + var entityData bytes.Buffer + if err := entity.Execute(&entityData, vars); err != nil { + return err + } + notification.Opsgenie.Entity = entityData.String() + + var userData bytes.Buffer + if err := user.Execute(&userData, vars); err != nil { + return err + } + notification.Opsgenie.User = userData.String() + + var priorityData bytes.Buffer + if err := priority.Execute(&priorityData, vars); err != nil { + return err + } + notification.Opsgenie.Priority = priorityData.String() + + if n.Details != nil { + notification.Opsgenie.Details = make(map[string]string, len(n.Details)) + for key, template := range details { + var valueData bytes.Buffer + if err := template.Execute(&valueData, vars); err != nil { + return err + } + notification.Opsgenie.Details[key] = valueData.String() + } + } + + if n.VisibleTo != nil { + notification.Opsgenie.VisibleTo = make([]alert.Responder, len(n.VisibleTo)) + for i, template := range visibleTo { + var idData, typeData, nameData, usernameData bytes.Buffer + + // Execute each responder field template + if err := template.Id.Execute(&idData, vars); err != nil { + return err + } + if err := template.Type.Execute(&typeData, vars); err != nil { + return err + } + if err := template.Name.Execute(&nameData, vars); err != nil { + return err + } + if err := template.Username.Execute(&usernameData, vars); err != nil { + return err + } + + notification.Opsgenie.VisibleTo[i] = alert.Responder{ + Id: idData.String(), + Type: alert.ResponderType(typeData.String()), // Convert the string to the ResponderType + Name: nameData.String(), + Username: usernameData.String(), + } + } + } + + if n.Actions != nil { + notification.Opsgenie.Actions = make([]string, len(n.Actions)) + for i, template := range actionsTemplates { + var actionData bytes.Buffer + if err := template.Execute(&actionData, vars); err != nil { + return err + } + notification.Opsgenie.Actions[i] = actionData.String() + } + } + + if n.Tags != nil { + notification.Opsgenie.Tags = make([]string, len(n.Tags)) + for i, template := range tagsTemplates { + var tagData bytes.Buffer + if err := template.Execute(&tagData, vars); err != nil { + return err + } + notification.Opsgenie.Tags[i] = tagData.String() + } + } + return nil }, nil } @@ -84,19 +258,19 @@ func (s *opsgenieService) Send(notification Notification, dest Destination) erro }, }) - var description, priority, alias, note string + var description, alias, note, entity, user string + var priority alert.Priority + var actions, tags []string + var details map[string]string + var visibleTo []alert.Responder if notification.Opsgenie != nil { if notification.Opsgenie.Description == "" { - return fmt.Errorf("Opsgenie notification description is missing") + return fmt.Errorf("opsgenie notification description is missing") } description = notification.Opsgenie.Description - if notification.Opsgenie.Priority != "" { - priority = notification.Opsgenie.Priority - } - if notification.Opsgenie.Alias != "" { alias = notification.Opsgenie.Alias } @@ -104,16 +278,48 @@ func (s *opsgenieService) Send(notification Notification, dest Destination) erro if notification.Opsgenie.Note != "" { note = notification.Opsgenie.Note } - } - alertPriority := alert.Priority(priority) + if notification.Opsgenie.Entity != "" { + entity = notification.Opsgenie.Entity + } + + if notification.Opsgenie.User != "" { + user = notification.Opsgenie.User + } + + if notification.Opsgenie.Priority != "" { + priority = alert.Priority(notification.Opsgenie.Priority) + } + + if len(notification.Opsgenie.Actions) > 0 { + actions = notification.Opsgenie.Actions + } + + if len(notification.Opsgenie.Tags) > 0 { + tags = notification.Opsgenie.Tags + } + + if len(notification.Opsgenie.Details) > 0 { + details = notification.Opsgenie.Details + } + + if len(notification.Opsgenie.VisibleTo) > 0 { + visibleTo = notification.Opsgenie.VisibleTo + } + } _, err := alertClient.Create(context.TODO(), &alert.CreateAlertRequest{ Message: notification.Message, Description: description, - Priority: alertPriority, + Priority: priority, Alias: alias, Note: note, + Actions: actions, + Tags: tags, + Details: details, + Entity: entity, + VisibleTo: visibleTo, + User: user, Responders: []alert.Responder{ { Type: "team", diff --git a/pkg/services/opsgenie_test.go b/pkg/services/opsgenie_test.go index 034aabe0..acb675fe 100644 --- a/pkg/services/opsgenie_test.go +++ b/pkg/services/opsgenie_test.go @@ -7,6 +7,7 @@ import ( "testing" texttemplate "text/template" + "github.com/opsgenie/opsgenie-go-sdk-v2/alert" "github.com/stretchr/testify/assert" ) @@ -38,16 +39,36 @@ func TestOpsgenieNotification_GetTemplater(t *testing.T) { // Prepare test data name := "testTemplate" descriptionTemplate := "Test Opsgenie alert: {{.foo}}" + priorityTemplate := "P1" aliasTemplate := "Test alias: {{.foo}}" noteTemplate := "Test note: {{.foo}}" + entityTemplate := "Test entity: {{.entity}}" + userTemplate := "Test user: {{.user}}" + actionsTemplate := []string{"action1: {{.foo}}", "action2: {{.foo}}"} + tagsTemplate := []string{"tag1: {{.foo}}", "tag2: {{.foo}}"} + detailsTemplate := map[string]string{ + "key1": "detail1: {{.foo}}", + "key2": "detail2: {{.foo}}", + } + visibleToTemplate := []alert.Responder{ + {Type: "user", Id: "{{.responder1}}", Name: "{{.responderName1}}", Username: "{{.username1}}"}, + {Type: "team", Id: "{{.responder2}}", Name: "{{.responderName2}}", Username: "{{.username2}}"}, + } f := texttemplate.FuncMap{} t.Run("ValidTemplate", func(t *testing.T) { // Create a new OpsgenieNotification instance notification := OpsgenieNotification{ Description: descriptionTemplate, + Priority: priorityTemplate, Alias: aliasTemplate, Note: noteTemplate, + Entity: entityTemplate, + User: userTemplate, + Actions: actionsTemplate, + Tags: tagsTemplate, + Details: detailsTemplate, + VisibleTo: visibleToTemplate, } // Call the GetTemplater method @@ -59,7 +80,15 @@ func TestOpsgenieNotification_GetTemplater(t *testing.T) { // Prepare mock data for the Templater function mockNotification := &Notification{} vars := map[string]interface{}{ - "foo": "bar", + "foo": "bar", + "entity": "entity1", + "user": "user1", + "responder1": "responder1_id", + "responderName1": "Responder One", + "username1": "responder1_username", + "responder2": "responder2_id", + "responderName2": "Responder Two", + "username2": "responder2_username", } // Call the Templater function returned by GetTemplater @@ -68,33 +97,106 @@ func TestOpsgenieNotification_GetTemplater(t *testing.T) { // Assert that no error occurred during the execution of the Templater function assert.NoError(t, err) - // Assert that the OpsgenieNotification's description field was correctly updated + // Assert that the OpsgenieNotification's fields were correctly updated assert.Equal(t, "Test Opsgenie alert: bar", mockNotification.Opsgenie.Description) - - // Assert that the OpsgenieNotification's alias field was correctly updated + assert.Equal(t, "P1", mockNotification.Opsgenie.Priority) assert.Equal(t, "Test alias: bar", mockNotification.Opsgenie.Alias) - - // Assert that the OpsgenieNotification's note field was correctly updated assert.Equal(t, "Test note: bar", mockNotification.Opsgenie.Note) + assert.Equal(t, "Test entity: entity1", mockNotification.Opsgenie.Entity) + assert.Equal(t, "Test user: user1", mockNotification.Opsgenie.User) + assert.Equal(t, []string{"action1: bar", "action2: bar"}, mockNotification.Opsgenie.Actions) + assert.Equal(t, []string{"tag1: bar", "tag2: bar"}, mockNotification.Opsgenie.Tags) + assert.Equal(t, map[string]string{ + "key1": "detail1: bar", + "key2": "detail2: bar", + }, mockNotification.Opsgenie.Details) + assert.Equal(t, []alert.Responder{ + {Type: "user", Id: "responder1_id", Name: "Responder One", Username: "responder1_username"}, + {Type: "team", Id: "responder2_id", Name: "Responder Two", Username: "responder2_username"}, + }, mockNotification.Opsgenie.VisibleTo) }) - t.Run("InvalidTemplateDescription", func(t *testing.T) { - // Create a new OpsgenieNotification instance with an invalid description template + t.Run("ValidTemplateDetails", func(t *testing.T) { + // Create a new OpsgenieNotification instance with a valid details template notification := OpsgenieNotification{ - Description: "{{.invalid", // Invalid template syntax + Details: map[string]string{ + "key1": "detail1: {{.foo}}", + "key2": "detail2: {{.foo}}", + }, } - // Call the GetTemplater method with the invalid template - _, err := notification.GetTemplater(name, f) + // Call the GetTemplater method + templater, err := notification.GetTemplater(name, f) - // Assert that an error occurred during the call - assert.Error(t, err) + // Assert that no error occurred during the call + assert.NoError(t, err) + + // Prepare mock data for the Templater function + mockNotification := &Notification{} + vars := map[string]interface{}{ + "foo": "bar", + } + + // Call the Templater function returned by GetTemplater + err = templater(mockNotification, vars) + + // Assert that no error occurred during the execution of the Templater function + assert.NoError(t, err) + + // Assert that the OpsgenieNotification's Details field was correctly updated + assert.Equal(t, map[string]string{ + "key1": "detail1: bar", + "key2": "detail2: bar", + }, mockNotification.Opsgenie.Details) }) - t.Run("InvalidTemplateAlias", func(t *testing.T) { - // Create a new OpsgenieNotification instance with an invalid alias template + t.Run("ValidTemplateVisibleTo", func(t *testing.T) { + // Create a new OpsgenieNotification instance with a valid visibleTo template notification := OpsgenieNotification{ - Alias: "{{.invalid", // Invalid template syntax + VisibleTo: []alert.Responder{ + {Type: "{{.responderType1}}", Id: "{{.responder1}}", Name: "{{.responderName1}}", Username: "{{.username1}}"}, + {Type: "{{.responderType2}}", Id: "{{.responder2}}", Name: "{{.responderName2}}", Username: "{{.username2}}"}, + }, + } + + // Call the GetTemplater method + templater, err := notification.GetTemplater(name, f) + + // Assert that no error occurred during the call + assert.NoError(t, err) + + // Prepare mock data for the Templater function + mockNotification := &Notification{} + vars := map[string]interface{}{ + "responderType1": "user", + "responder1": "responder1_id", + "responderName1": "Responder One", + "username1": "responder1_username", + "responderType2": "team", + "responder2": "responder2_id", + "responderName2": "Responder Two", + "username2": "responder2_username", + } + + // Call the Templater function returned by GetTemplater + err = templater(mockNotification, vars) + + // Assert that no error occurred during the execution of the Templater function + assert.NoError(t, err) + + // Assert that the OpsgenieNotification's VisibleTo field was correctly updated + assert.Equal(t, []alert.Responder{ + {Type: "user", Id: "responder1_id", Name: "Responder One", Username: "responder1_username"}, + {Type: "team", Id: "responder2_id", Name: "Responder Two", Username: "responder2_username"}, + }, mockNotification.Opsgenie.VisibleTo) + }) + + t.Run("InvalidTemplateDetails", func(t *testing.T) { + // Create a new OpsgenieNotification instance with an invalid details template + notification := OpsgenieNotification{ + Details: map[string]string{ + "key1": "{{.invalid", // Invalid template syntax + }, } // Call the GetTemplater method with the invalid template @@ -104,10 +206,12 @@ func TestOpsgenieNotification_GetTemplater(t *testing.T) { assert.Error(t, err) }) - t.Run("InvalidTemplateNote", func(t *testing.T) { - // Create a new OpsgenieNotification instance with an invalid note template + t.Run("InvalidTemplateVisibleTo", func(t *testing.T) { + // Create a new OpsgenieNotification instance with an invalid visibleTo template notification := OpsgenieNotification{ - Note: "{{.invalid", // Invalid template syntax + VisibleTo: []alert.Responder{ + {Id: "{{.invalid"}, // Invalid template syntax + }, } // Call the GetTemplater method with the invalid template @@ -116,6 +220,42 @@ func TestOpsgenieNotification_GetTemplater(t *testing.T) { // Assert that an error occurred during the call assert.Error(t, err) }) + + t.Run("ValidTemplateTags", func(t *testing.T) { + // Create a new OpsgenieNotification instance + notification := OpsgenieNotification{ + Description: "Test Opsgenie alert: {{.foo}}", + Priority: "P1", + Alias: "Test alias: {{.foo}}", + Note: "Test note: {{.foo}}", + Actions: []string{"action1: {{.foo}}", "action2: {{.foo}}"}, + Tags: []string{"tag1: {{.foo}}", "tag2: {{.foo}}"}, + } + + // Call the GetTemplater method + templater, err := notification.GetTemplater(name, f) + + // Assert that no error occurred during the call + assert.NoError(t, err) + + // Prepare mock data for the Templater function + mockNotification := &Notification{} + vars := map[string]interface{}{ + "foo": "bar", + } + + // Call the Templater function returned by GetTemplater + err = templater(mockNotification, vars) + + // Assert that no error occurred during the execution of the Templater function + assert.NoError(t, err) + + // Assert that the OpsgenieNotification's actions field was correctly updated + assert.Equal(t, []string{"action1: bar", "action2: bar"}, mockNotification.Opsgenie.Actions) + + // Assert that the OpsgenieNotification's tags field was correctly updated + assert.Equal(t, []string{"tag1: bar", "tag2: bar"}, mockNotification.Opsgenie.Tags) + }) } func TestOpsgenie_SendNotification_MissingAPIKey(t *testing.T) { @@ -133,6 +273,7 @@ func TestOpsgenie_SendNotification_MissingAPIKey(t *testing.T) { recipient := "testRecipient" message := "Test message" descriptionTemplate := "Test Opsgenie alert: {{.foo}}" + priority := "P1" aliasTemplate := "Test alias: {{.foo}}" noteTemplate := "Test note: {{.foo}}" @@ -141,6 +282,7 @@ func TestOpsgenie_SendNotification_MissingAPIKey(t *testing.T) { Message: message, Opsgenie: &OpsgenieNotification{ Description: descriptionTemplate, + Priority: priority, Alias: aliasTemplate, Note: noteTemplate, }, @@ -288,8 +430,21 @@ func TestOpsgenie_SendNotification_WithAllFields(t *testing.T) { aliasTemplate := "Test alias: {{.foo}}" priority := "P1" noteTemplate := "Test note: {{.foo}}" + actions := []string{"action1", "action2"} + tags := []string{"tag1", "tag2"} + details := map[string]string{"detail1": "value1"} + entity := "TestEntity" + user := "TestUser" + + // Testing multiple responder scenarios with templates + visibleTo := []alert.Responder{ + {Type: "user", Id: "{{.responderUserId}}"}, // Template for user responder + {Type: "team", Id: "{{.responderTeamId}}"}, // Template for team responder + {Type: "escalation", Id: "{{.responderEscalationId}}"}, // Template for escalation responder + {Type: "schedule", Id: "{{.responderScheduleId}}"}, // Template for schedule responder + } - // Create test notification with description and priority + // Create test notification with all fields notification := Notification{ Message: message, Opsgenie: &OpsgenieNotification{ @@ -297,12 +452,18 @@ func TestOpsgenie_SendNotification_WithAllFields(t *testing.T) { Priority: priority, Alias: aliasTemplate, Note: noteTemplate, + Actions: actions, + Tags: tags, + Details: details, + Entity: entity, + User: user, + VisibleTo: visibleTo, }, } - // Execute the service method with description and priority + // Execute the service method with all fields err := service.Send(notification, Destination{Recipient: recipient, Service: "opsgenie"}) - // Assert the result for description and priority present + // Assert the result for all fields present assert.NoError(t, err) // Expect no error }