From 91101c821cbfa3c22195000232c66203ea4d4b4f Mon Sep 17 00:00:00 2001 From: Holger Waschke Date: Tue, 21 Oct 2025 16:55:55 +0200 Subject: [PATCH] feat(jira.go): allow configuring issue update via parameter Signed-off-by: Holger Waschke --- config/notifiers.go | 9 +++--- notify/jira/jira.go | 4 +++ notify/jira/jira_test.go | 61 ++++++++++++++++++++++++++++++++++++++++ notify/jira/types.go | 8 ++++-- 4 files changed, 75 insertions(+), 7 deletions(-) diff --git a/config/notifiers.go b/config/notifiers.go index d829efa39f..7c6df6a6d6 100644 --- a/config/notifiers.go +++ b/config/notifiers.go @@ -976,10 +976,11 @@ type JiraConfig struct { Priority string `yaml:"priority,omitempty" json:"priority,omitempty"` IssueType string `yaml:"issue_type,omitempty" json:"issue_type,omitempty"` - ReopenTransition string `yaml:"reopen_transition,omitempty" json:"reopen_transition,omitempty"` - ResolveTransition string `yaml:"resolve_transition,omitempty" json:"resolve_transition,omitempty"` - WontFixResolution string `yaml:"wont_fix_resolution,omitempty" json:"wont_fix_resolution,omitempty"` - ReopenDuration model.Duration `yaml:"reopen_duration,omitempty" json:"reopen_duration,omitempty"` + ReopenTransition string `yaml:"reopen_transition,omitempty" json:"reopen_transition,omitempty"` + ResolveTransition string `yaml:"resolve_transition,omitempty" json:"resolve_transition,omitempty"` + WontFixResolution string `yaml:"wont_fix_resolution,omitempty" json:"wont_fix_resolution,omitempty"` + ReopenDuration model.Duration `yaml:"reopen_duration,omitempty" json:"reopen_duration,omitempty"` + DisableUpdateDescription bool `yaml:"disable_update_description,omitempty" json:"disable_update_description,omitempty"` Fields map[string]any `yaml:"fields,omitempty" json:"custom_fields,omitempty"` } diff --git a/notify/jira/jira.go b/notify/jira/jira.go index 5d91382fe4..6f20496975 100644 --- a/notify/jira/jira.go +++ b/notify/jira/jira.go @@ -111,6 +111,10 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error) return false, err } + if method == http.MethodPut && requestBody.Fields != nil && n.conf.DisableUpdateDescription { + requestBody.Fields.Description = nil + } + _, shouldRetry, err = n.doAPIRequest(ctx, method, path, requestBody) if err != nil { return shouldRetry, fmt.Errorf("failed to %s request to %q: %w", method, path, err) diff --git a/notify/jira/jira_test.go b/notify/jira/jira_test.go index 6abc1671e0..d2fb647b75 100644 --- a/notify/jira/jira_test.go +++ b/notify/jira/jira_test.go @@ -747,6 +747,66 @@ func TestJiraNotify(t *testing.T) { customFieldAssetFn: func(t *testing.T, issue map[string]any) {}, errMsg: "can't find transition REOPEN for issue OPS-3", }, + { + title: "update existing issue when setting disableupdatedescription to true", + cfg: &config.JiraConfig{ + Summary: `{{ template "jira.default.summary" . }}`, + Description: `{{ template "jira.default.description" . }}`, + IssueType: "Incident", + Project: "OPS", + Priority: `{{ template "jira.default.priority" . }}`, + Labels: []string{"alertmanager", "{{ .GroupLabels.alertname }}"}, + DisableUpdateDescription: true, + }, + alert: &types.Alert{ + Alert: model.Alert{ + Labels: model.LabelSet{ + "alertname": "test", + "instance": "vm1", + "severity": "critical", + }, + StartsAt: time.Now(), + EndsAt: time.Now().Add(time.Hour), + }, + }, + searchResponse: issueSearchResult{ + Issues: []issue{ + { + Key: "OPS-4", + Fields: &issueFields{ + Status: &issueStatus{ + Name: "Open", + StatusCategory: struct { + Key string `json:"key"` + }{ + Key: "open", + }, + }, + }, + }, + }, + }, + issue: issue{ + Key: "", + Fields: &issueFields{ + Summary: "[FIRING:1] test (vm1 critical)", + Description: nil, + Issuetype: &idNameValue{Name: "Incident"}, + Labels: []string{ + "ALERT{6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b}", + "alertmanager", + "test", + }, + Project: &issueProject{Key: "OPS"}, + Priority: &idNameValue{Name: "High"}, + }, + }, + customFieldAssetFn: func(t *testing.T, fields map[string]any) { + _, has := fields["description"] + require.False(t, has, "description field must be omitted on update when UpdateDescription=false") + }, + errMsg: "", + }, } { tc := tc @@ -841,6 +901,7 @@ func TestJiraNotify(t *testing.T) { case "/issue/OPS-1": case "/issue/OPS-2": case "/issue/OPS-3": + case "/issue/OPS-4": fallthrough case "/issue": body, err := io.ReadAll(r.Body) diff --git a/notify/jira/types.go b/notify/jira/types.go index e04eb6d4f8..3c6be6ca45 100644 --- a/notify/jira/types.go +++ b/notify/jira/types.go @@ -70,11 +70,13 @@ type issueTransitions struct { // MarshalJSON merges the struct issueFields and issueFields.CustomField together. func (i issueFields) MarshalJSON() ([]byte, error) { - jsonFields := map[string]any{ - "description": i.Description, - "summary": i.Summary, + jsonFields := map[string]interface{}{ + "summary": i.Summary, } + if i.Description != nil { + jsonFields["description"] = i.Description + } if i.Issuetype != nil { jsonFields["issuetype"] = i.Issuetype }