@@ -28,7 +28,6 @@ import (
28
28
"github.com/go-kit/log/level"
29
29
commoncfg "github.com/prometheus/common/config"
30
30
"github.com/prometheus/common/model"
31
- "github.com/trivago/tgo/tcontainer"
32
31
33
32
"github.com/prometheus/alertmanager/config"
34
33
"github.com/prometheus/alertmanager/notify"
@@ -120,17 +119,25 @@ func (n *Notifier) Notify(ctx context.Context, as ...*types.Alert) (bool, error)
120
119
return n .transitionIssue (ctx , logger , existingIssue , alerts .HasFiring ())
121
120
}
122
121
123
- func (n * Notifier ) prepareIssueRequestBody (ctx context.Context , logger log.Logger , groupID string , tmplTextFunc templateFunc ) (issue , error ) {
122
+ func (n * Notifier ) prepareIssueRequestBody (_ context.Context , logger log.Logger , groupID string , tmplTextFunc templateFunc ) (issue , error ) {
124
123
summary , err := tmplTextFunc (n .conf .Summary )
125
124
if err != nil {
126
125
return issue {}, fmt .Errorf ("summary template: %w" , err )
127
126
}
128
127
129
- // Recursively convert any maps to map[string]interface{}, filtering out all non-string keys, so the json encoder
130
- // doesn't blow up when marshaling JIRA requests.
131
- fieldsWithStringKeys , err := tcontainer .ConvertToMarshalMap (n .conf .Fields , func (v string ) string { return v })
132
- if err != nil {
133
- return issue {}, fmt .Errorf ("convertToMarshalMap: %w" , err )
128
+ fields := make (map [string ]any )
129
+ for name , field := range n .conf .Fields {
130
+ templatedFieldValue , err := tmplTextFunc (field )
131
+ if err != nil {
132
+ return issue {}, fmt .Errorf ("field %q template: %w" , name , err )
133
+ }
134
+
135
+ parsedValue , err := n .unmarshalField (templatedFieldValue )
136
+ if err != nil {
137
+ return issue {}, fmt .Errorf ("field %q conversion: %w" , name , err )
138
+ }
139
+
140
+ fields [name ] = parsedValue
134
141
}
135
142
136
143
summary , truncated := notify .TruncateInRunes (summary , maxSummaryLenRunes )
@@ -143,7 +150,7 @@ func (n *Notifier) prepareIssueRequestBody(ctx context.Context, logger log.Logge
143
150
Issuetype : & idNameValue {Name : n .conf .IssueType },
144
151
Summary : summary ,
145
152
Labels : make ([]string , 0 , len (n .conf .Labels )+ 1 ),
146
- Fields : fieldsWithStringKeys ,
153
+ Fields : fields ,
147
154
}}
148
155
149
156
issueDescriptionString , err := tmplTextFunc (n .conf .Description )
@@ -172,6 +179,7 @@ func (n *Notifier) prepareIssueRequestBody(ctx context.Context, logger log.Logge
172
179
}
173
180
requestBody .Fields .Labels = append (requestBody .Fields .Labels , label )
174
181
}
182
+
175
183
requestBody .Fields .Labels = append (requestBody .Fields .Labels , fmt .Sprintf ("ALERT{%s}" , groupID ))
176
184
sort .Strings (requestBody .Fields .Labels )
177
185
@@ -187,6 +195,74 @@ func (n *Notifier) prepareIssueRequestBody(ctx context.Context, logger log.Logge
187
195
return requestBody , nil
188
196
}
189
197
198
+ func (n * Notifier ) unmarshalField (fieldValue any ) (any , error ) {
199
+ // Handle type based on input directly without casting to string
200
+ switch fieldValueTyped := fieldValue .(type ) {
201
+ case string :
202
+ if len (fieldValueTyped ) == 0 {
203
+ return fieldValueTyped , nil
204
+ }
205
+
206
+ // Try to parse as JSON. This includes the handling of strings, numbers, arrays, and maps.
207
+ var parsedJSON any
208
+ if err := json .Unmarshal ([]byte (fieldValueTyped ), & parsedJSON ); err == nil {
209
+ // if the JSON is a string, return it as a string.
210
+ // Otherwise, numeric values inside strings are converted to float64.
211
+ if parsedString , ok := parsedJSON .(string ); ok {
212
+ return parsedString , nil
213
+ }
214
+
215
+ return n .unmarshalField (parsedJSON )
216
+ }
217
+
218
+ // If no type conversion was possible, keep it as a string
219
+ return fieldValueTyped , nil
220
+ case []any :
221
+ // Handle arrays by recursively parsing each element
222
+ fieldValues := make ([]any , len (fieldValueTyped ))
223
+ for i , elem := range fieldValueTyped {
224
+ // if the JSON is a string, return it as a string.
225
+ // Otherwise, numeric values inside strings are converted to float64.
226
+ if v , ok := elem .(string ); ok {
227
+ fieldValues [i ] = v
228
+ continue
229
+ }
230
+
231
+ parsedElem , err := n .unmarshalField (elem )
232
+ if err != nil {
233
+ return nil , err
234
+ }
235
+
236
+ fieldValues [i ] = parsedElem
237
+ }
238
+
239
+ return fieldValues , nil
240
+ case map [string ]any :
241
+ // Handle maps by recursively parsing each value
242
+ for key , val := range fieldValueTyped {
243
+ // if the JSON is a string, return it as a string.
244
+ // Otherwise, numeric values inside strings are converted to float64.
245
+ if stringVal , ok := val .(string ); ok {
246
+ fieldValueTyped [key ] = stringVal
247
+
248
+ continue
249
+ }
250
+
251
+ parsedVal , err := n .unmarshalField (val )
252
+ if err != nil {
253
+ return nil , err
254
+ }
255
+
256
+ fieldValueTyped [key ] = parsedVal
257
+ }
258
+
259
+ return fieldValueTyped , nil
260
+ default :
261
+ // Return the value as-is if no specific handling is required
262
+ return fieldValueTyped , nil
263
+ }
264
+ }
265
+
190
266
func (n * Notifier ) searchExistingIssue (ctx context.Context , logger log.Logger , groupID string , firing bool ) (* issue , bool , error ) {
191
267
jql := strings.Builder {}
192
268
0 commit comments