Commit Diff


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 + "..."
+}