jira.go (4069B)
1 package jira 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "time" 7 ) 8 9 const timestamp = "2006-01-02T15:04:05.999-0700" 10 11 // Issue represents a limited subset of a Jira issue's data. 12 // For a complete example of all issue data, 13 // see https://jira.atlassian.com/rest/api/2/issue/JRA-9. 14 type Issue struct { 15 ID string `json:"id,omitempty"` // TODO(otl): int? 16 URL string `json:"url,omitempty"` 17 Key string `json:"key,omitempty"` 18 Reporter *User `json:"reporter,omitempty"` 19 Assignee *User `json:"assignee,omitempty"` 20 Summary string `json:"summary"` 21 Status *struct { 22 Name string `json:"name"` 23 } `json:"status,omitempty"` 24 Description string `json:"description"` 25 Project Project `json:"project"` 26 Created time.Time `json:"created,omitzero"` 27 Updated time.Time `json:"updated,omitzero"` 28 Due time.Time `json:"duedate,omitzero"` 29 Type struct { 30 Name string `json:"name"` 31 } `json:"issuetype"` 32 Comments []Comment `json:"comments,omitempty"` 33 Links []Issue `json:"links,omitempty"` 34 Subtasks []Issue `json:"subtasks,omitempty"` 35 } 36 37 type Project struct { 38 ID string `json:"id,omitempty"` // TODO(otl): int? 39 // Name string `json:"name"` 40 Key string `json:"key,omitempty"` 41 URL string `json:"self,omitempty"` 42 } 43 44 type Comment struct { 45 ID string `json:"id"` // TODO(otl): int? 46 URL string `json:"self"` 47 Body string `json:"body"` 48 Created time.Time `json:"created"` 49 Updated time.Time `json:"updated"` 50 Author User `json:"author"` 51 UpdateAuthor User `json:"updateAuthor"` 52 } 53 54 func (c *Comment) UnmarshalJSON(b []byte) error { 55 type alias Comment 56 aux := &struct { 57 Created string `json:"created"` 58 Updated string `json:"updated"` 59 *alias 60 }{ 61 alias: (*alias)(c), 62 } 63 if err := json.Unmarshal(b, aux); err != nil { 64 return err 65 } 66 var err error 67 c.Created, err = time.Parse(timestamp, aux.Created) 68 if err != nil { 69 return fmt.Errorf("parse created time: %w", err) 70 } 71 c.Updated, err = time.Parse(timestamp, aux.Updated) 72 if err != nil { 73 return fmt.Errorf("parse updated time: %w", err) 74 } 75 return nil 76 } 77 78 type User struct { 79 Name string `json:"name"` 80 Email string `json:"emailAddress"` 81 DisplayName string `json:"displayName"` 82 } 83 84 func (u User) String() string { 85 if u.DisplayName == "" { 86 return u.Email 87 } 88 return fmt.Sprintf("%s <%s>", u.DisplayName, u.Email) 89 } 90 91 func (issue *Issue) UnmarshalJSON(b []byte) error { 92 aux := &struct { 93 ID string 94 Self string 95 Key string 96 Fields json.RawMessage 97 }{} 98 if err := json.Unmarshal(b, aux); err != nil { 99 return err 100 } 101 issue.ID = aux.ID 102 issue.URL = aux.Self 103 issue.Key = aux.Key 104 105 type alias Issue 106 iaux := &struct { 107 Created string `json:"created"` 108 Updated string `json:"updated"` 109 Due string `json:"duedate"` 110 Comment map[string]json.RawMessage 111 IssueLinks []struct { 112 InwardIssue *Issue 113 OutwardIssue *Issue 114 } 115 *alias 116 }{ 117 alias: (*alias)(issue), 118 } 119 if err := json.Unmarshal(aux.Fields, iaux); err != nil { 120 return err 121 } 122 123 var err error 124 if iaux.Created != "" { 125 issue.Created, err = time.Parse(timestamp, iaux.Created) 126 if err != nil { 127 return fmt.Errorf("created time: %w", err) 128 } 129 } 130 if iaux.Updated != "" { 131 issue.Updated, err = time.Parse(timestamp, iaux.Updated) 132 if err != nil { 133 return fmt.Errorf("updated time: %w", err) 134 } 135 } 136 if iaux.Due != "" { 137 issue.Due, err = time.Parse(timestamp, iaux.Due) 138 if err != nil { 139 return fmt.Errorf("due date: %w", err) 140 } 141 } 142 if bb, ok := iaux.Comment["comments"]; ok { 143 if err := json.Unmarshal(bb, &issue.Comments); err != nil { 144 return fmt.Errorf("unmarshal comments: %w", err) 145 } 146 } 147 for _, l := range iaux.IssueLinks { 148 if l.InwardIssue != nil { 149 issue.Links = append(issue.Links, *l.InwardIssue) 150 } 151 if l.OutwardIssue != nil { 152 issue.Links = append(issue.Links, *l.OutwardIssue) 153 } 154 } 155 return nil 156 } 157 158 func (issue *Issue) MarshalJSON() ([]byte, error) { 159 type alias Issue 160 aux := struct { 161 Fields *alias `json:"fields"` 162 }{(*alias)(issue)} 163 return json.Marshal(aux) 164 }