commit ba9e266535f5c1704c277e5402192008034adc16 from: Oliver Lowe date: Sun Nov 03 22:21:57 2024 UTC jira: printing commit - 8d9dbdfc8a07bca44bcef1a030dc1e248a6ddeaf commit + ba9e266535f5c1704c277e5402192008034adc16 blob - b83bcf3e4b98697cc1693190dc78ba336010b40a blob + 0e557dbc3006d3655385cd682f92608262fdf8e2 --- jira/jira.go +++ jira/jira.go @@ -1,4 +1,5 @@ -// +// https://developer.atlassian.com/cloud/jira/platform/rest/v2/ +// https://jira.atlassian.com/rest/api/latest/issue/JRA-9 package main import ( @@ -7,12 +8,18 @@ import ( "time" ) +const timestamp = "2006-01-02T15:04:05.999-0700" + type Issue struct { - ID string `json:"id"` // TODO(otl): int? - URL string `json:"self"` - Key string `json:"key"` + ID string // TODO(otl): int? + URL string + Key string + Reporter User + Summary string Description string Project Project + Created time.Time + Updated time.Time Comments []Comment } @@ -23,8 +30,8 @@ type Project struct { } type Comment struct { - URL string `json:"self"` ID string `json:"id"` // TODO(otl): int? + URL string `json:"self"` Body string `json:"body"` Created time.Time `json:"created"` Updated time.Time `json:"updated"` @@ -45,12 +52,11 @@ func (c *Comment) UnmarshalJSON(b []byte) error { return err } var err error - tstamp := "2006-01-02T15:04:05.999-0700" - c.Created, err = time.Parse(tstamp, aux.Created) + c.Created, err = time.Parse(timestamp, aux.Created) if err != nil { return fmt.Errorf("parse created time: %w", err) } - c.Updated, err = time.Parse(tstamp, aux.Updated) + c.Updated, err = time.Parse(timestamp, aux.Updated) if err != nil { return fmt.Errorf("parse updated time: %w", err) } @@ -63,22 +69,46 @@ type User struct { } func (issue *Issue) UnmarshalJSON(b []byte) error { - type alias Issue aux := &struct { - Fields struct { - Description string - Comment []Comment - Project Project - } + ID string + Self string + Key string + Fields json.RawMessage + }{} + if err := json.Unmarshal(b, aux); err != nil { + return err + } + issue.ID = aux.ID + issue.URL = aux.Self + issue.Key = aux.Key + + type alias Issue + iaux := &struct { + Created string + Updated string + Comment map[string]json.RawMessage *alias }{ alias: (*alias)(issue), } - if err := json.Unmarshal(b, aux); err != nil { + if err := json.Unmarshal(aux.Fields, iaux); err != nil { return err } - issue.Comments = aux.Fields.Comment - issue.Description = aux.Fields.Description - issue.Project = aux.Fields.Project + + var err error + issue.Created, err = time.Parse(timestamp, iaux.Created) + if err != nil { + return fmt.Errorf("created time: %w", err) + } + issue.Updated, err = time.Parse(timestamp, iaux.Updated) + if err != nil { + return fmt.Errorf("updated time: %w", err) + } + + if bb, ok := iaux.Comment["comments"]; ok { + if err := json.Unmarshal(bb, &issue.Comments); err != nil { + return fmt.Errorf("unmarshal comments: %w", err) + } + } return nil } blob - 7b7992b9e2273f94ef78eb36746b8d7bc5e7d6e2 blob + 228ee2633969341e9376cc0ca2f9173fa531c45a --- jira/jira_test.go +++ jira/jira_test.go @@ -3,30 +3,24 @@ package main import ( "encoding/json" "os" + "path/filepath" "testing" ) func TestIssue(t *testing.T) { - f, err := os.Open("issue.json") + names, err := filepath.Glob("issue*.json") if err != nil { t.Fatal(err) } - defer f.Close() - var issue Issue - if err := json.NewDecoder(f).Decode(&issue); err != nil { - t.Fatal(err) + for _, name := range names { + f, err := os.Open(name) + if err != nil { + t.Fatalf("%s: %v", name, err) + } + defer f.Close() + var issue Issue + if err := json.NewDecoder(f).Decode(&issue); err != nil { + t.Fatalf("%s: %v", name, err) + } } } - -func TestPrint(t *testing.T) { - f, err := os.Open("issue.json") - if err != nil { - t.Fatal(err) - } - defer f.Close() - var issue Issue - if err := json.NewDecoder(f).Decode(&issue); err != nil { - t.Fatal(err) - } - printIssue(os.Stdout, &issue) -} blob - b8598ddab249a4c773596c165ff8186cd28c2ab6 blob + 6a0a9f61dbdc6dfb8597452dab7845f69dbab1e4 --- jira/print.go +++ jira/print.go @@ -4,19 +4,24 @@ import ( "fmt" "io" "strings" + "time" ) func printIssue(w io.Writer, i *Issue) (n int, err error) { buf := &strings.Builder{} + fmt.Fprintln(buf, "From:", i.Reporter.Name) fmt.Fprintln(buf, "URL:", i.URL) + fmt.Fprintln(buf, "Subject:", i.Summary) fmt.Fprintln(buf) fmt.Fprintln(buf, i.Description) fmt.Fprintln(buf) - for _, c := range i.Comments { - buf.WriteString("\t") - printComment(buf, &c) + date := c.Created + if !c.Updated.IsZero() { + date = c.Updated + } + fmt.Fprintf(buf, "%s/\t%s\t%s (%s)\n", c.ID, summarise(c.Body), c.Author.Name, date.Format(time.DateTime)) } return w.Write([]byte(buf.String())) } @@ -33,3 +38,17 @@ func printComment(w io.Writer, c *Comment) (n int, err fmt.Fprintln(buf, c.Body) return w.Write([]byte(buf.String())) } + +func summarise(body string) string { + max := 36 + if len(body) < max { + body = strings.ReplaceAll(body, "\n", " ") + return strings.TrimSpace(body) + } + body = body[:max] + body = strings.ReplaceAll(body, "\r", "") + body = strings.ReplaceAll(body, "\n", " ") + body = strings.TrimSpace(body) + body = strings.ReplaceAll(body, " ", " ") + return body + "..." +}