@@ -2,6 +2,7 @@ package jira
22
33import (
44 "context"
5+ "encoding/json"
56 "fmt"
67 "os"
78 "strconv"
@@ -22,14 +23,16 @@ func init() {
2223
2324// Tracker implements tracker.IssueTracker for Jira.
2425type Tracker struct {
25- client * Client
26- store storage.Storage
27- jiraURL string
28- projectKeys []string // one or more project keys (first is primary)
29- apiVersion string // "2" or "3" (default: "3")
30- statusMap map [string ]string // beads status → Jira status name (from jira.status_map.* config)
31- typeMap map [string ]string // beads type → Jira type (from jira.type_map.* config)
32- priorityMap map [string ]string // beads priority → Jira priority name (from jira.priority_map.* config)
26+ client * Client
27+ store storage.Storage
28+ jiraURL string
29+ projectKeys []string // one or more project keys (first is primary)
30+ apiVersion string // "2" or "3" (default: "3")
31+ statusMap map [string ]string // beads status → Jira status name (from jira.status_map.* config)
32+ typeMap map [string ]string // beads type → Jira type (from jira.type_map.* config)
33+ priorityMap map [string ]string // beads priority → Jira priority name (from jira.priority_map.* config)
34+ customFields map [string ]interface {} // Jira field name/id → value (from jira.custom_fields.* config)
35+ typeCustomFields map [string ]map [string ]interface {} // Jira issue type → Jira field name/id → value
3336}
3437
3538// SetProjectKeys sets project keys before Init(). When set, Init() uses these
@@ -124,6 +127,44 @@ func (t *Tracker) Init(ctx context.Context, store storage.Storage) error {
124127 if len (priorityMap ) > 0 {
125128 t .priorityMap = priorityMap
126129 }
130+
131+ const customFieldPrefix = "jira.custom_fields."
132+ customFields := make (map [string ]interface {})
133+ typeCustomFields := make (map [string ]map [string ]interface {})
134+ for key , val := range allConfig {
135+ if ! strings .HasPrefix (key , customFieldPrefix ) || strings .TrimSpace (val ) == "" {
136+ continue
137+ }
138+
139+ suffix := strings .TrimPrefix (key , customFieldPrefix )
140+ if suffix == "" {
141+ continue
142+ }
143+
144+ parsed , err := parseJiraCustomFieldValue (val )
145+ if err != nil {
146+ return fmt .Errorf ("parse %s: %w" , key , err )
147+ }
148+
149+ parts := strings .SplitN (suffix , "." , 2 )
150+ if len (parts ) == 2 {
151+ if parts [0 ] == "" || parts [1 ] == "" {
152+ continue
153+ }
154+ if typeCustomFields [parts [0 ]] == nil {
155+ typeCustomFields [parts [0 ]] = make (map [string ]interface {})
156+ }
157+ typeCustomFields [parts [0 ]][parts [1 ]] = parsed
158+ continue
159+ }
160+ customFields [suffix ] = parsed
161+ }
162+ if len (customFields ) > 0 {
163+ t .customFields = customFields
164+ }
165+ if len (typeCustomFields ) > 0 {
166+ t .typeCustomFields = typeCustomFields
167+ }
127168 }
128169
129170 return nil
@@ -273,7 +314,14 @@ func (t *Tracker) applyTransition(ctx context.Context, key string, status types.
273314}
274315
275316func (t * Tracker ) FieldMapper () tracker.FieldMapper {
276- return & jiraFieldMapper {apiVersion : t .apiVersion , statusMap : t .statusMap , typeMap : t .typeMap , priorityMap : t .priorityMap }
317+ return & jiraFieldMapper {
318+ apiVersion : t .apiVersion ,
319+ statusMap : t .statusMap ,
320+ typeMap : t .typeMap ,
321+ priorityMap : t .priorityMap ,
322+ customFields : t .customFields ,
323+ typeCustomFields : t .typeCustomFields ,
324+ }
277325}
278326
279327func (t * Tracker ) IsExternalRef (ref string ) bool {
@@ -318,6 +366,21 @@ func (t *Tracker) getConfig(ctx context.Context, key, envVar string) (string, er
318366 return "" , nil
319367}
320368
369+ func parseJiraCustomFieldValue (value string ) (interface {}, error ) {
370+ trimmed := strings .TrimSpace (value )
371+ if trimmed == "" {
372+ return "" , nil
373+ }
374+ if strings .HasPrefix (trimmed , "{" ) || strings .HasPrefix (trimmed , "[" ) {
375+ var parsed interface {}
376+ if err := json .Unmarshal ([]byte (trimmed ), & parsed ); err != nil {
377+ return nil , err
378+ }
379+ return parsed , nil
380+ }
381+ return trimmed , nil
382+ }
383+
321384// jiraToTrackerIssue converts a Jira API Issue to the generic TrackerIssue format.
322385// priorityMap is optional (nil uses hardcoded defaults).
323386func jiraToTrackerIssue (ji * Issue , priorityMap map [string ]string ) tracker.TrackerIssue {
0 commit comments