Commit Diff


commit - 72ce55ca9db6e1433ac2c235c5e72c3f2feab374
commit + 8d9dbdfc8a07bca44bcef1a030dc1e248a6ddeaf
blob - /dev/null
blob + be2c7243af97955bd988833827120bab346d0c2b (mode 644)
--- /dev/null
+++ jira/issue.json
@@ -0,0 +1,204 @@
+{
+    "expand": "renderedFields,names,schema,operations,editmeta,changelog,versionedRepresentations",
+    "id": "10002",
+    "self": "http://www.example.com/jira/rest/api/2/issue/10002",
+    "key": "EX-1",
+    "names": {
+        "watcher": "watcher",
+        "attachment": "attachment",
+        "sub-tasks": "sub-tasks",
+        "description": "description",
+        "project": "project",
+        "comment": "comment",
+        "issuelinks": "issuelinks",
+        "worklog": "worklog",
+        "updated": "updated",
+        "timetracking": "timetracking"
+    },
+    "schema": {},
+    "fields": {
+        "watcher": {
+            "self": "http://www.example.com/jira/rest/api/2/issue/EX-1/watchers",
+            "isWatching": false,
+            "watchCount": 1,
+            "watchers": [
+                {
+                    "self": "http://www.example.com/jira/rest/api/2/user?username=fred",
+                    "name": "fred",
+                    "displayName": "Fred F. User",
+                    "active": false
+                }
+            ]
+        },
+        "attachment": [
+            {
+                "self": "http://www.example.com/jira/rest/api/2.0/attachments/10000",
+                "filename": "picture.jpg",
+                "author": {
+                    "self": "http://www.example.com/jira/rest/api/2/user?username=fred",
+                    "key": "fred",
+                    "accountId": "99:27935d01-92a7-4687-8272-a9b8d3b2ae2e",
+                    "name": "fred",
+                    "avatarUrls": {
+                        "48x48": "http://www.example.com/jira/secure/useravatar?size=large&ownerId=fred",
+                        "24x24": "http://www.example.com/jira/secure/useravatar?size=small&ownerId=fred",
+                        "16x16": "http://www.example.com/jira/secure/useravatar?size=xsmall&ownerId=fred",
+                        "32x32": "http://www.example.com/jira/secure/useravatar?size=medium&ownerId=fred"
+                    },
+                    "displayName": "Fred F. User",
+                    "active": false
+                },
+                "created": "2017-03-08T03:29:43.525+0000",
+                "size": 23123,
+                "mimeType": "image/jpeg",
+                "content": "http://www.example.com/jira/attachments/10000",
+                "thumbnail": "http://www.example.com/jira/secure/thumbnail/10000"
+            }
+        ],
+        "sub-tasks": [
+            {
+                "id": "10000",
+                "type": {
+                    "id": "10000",
+                    "name": "",
+                    "inward": "Parent",
+                    "outward": "Sub-task"
+                },
+                "outwardIssue": {
+                    "id": "10003",
+                    "key": "EX-2",
+                    "self": "http://www.example.com/jira/rest/api/2/issue/EX-2",
+                    "fields": {
+                        "status": {
+                            "iconUrl": "http://www.example.com/jira//images/icons/statuses/open.png",
+                            "name": "Open"
+                        }
+                    }
+                }
+            }
+        ],
+        "description": "example bug report",
+        "project": {
+            "self": "http://www.example.com/jira/rest/api/2/project/EX",
+            "id": "10000",
+            "key": "EX",
+            "name": "Example",
+            "avatarUrls": {
+                "48x48": "http://www.example.com/jira/secure/projectavatar?size=large&pid=10000",
+                "24x24": "http://www.example.com/jira/secure/projectavatar?size=small&pid=10000",
+                "16x16": "http://www.example.com/jira/secure/projectavatar?size=xsmall&pid=10000",
+                "32x32": "http://www.example.com/jira/secure/projectavatar?size=medium&pid=10000"
+            },
+            "projectCategory": {
+                "self": "http://www.example.com/jira/rest/api/2/projectCategory/10000",
+                "id": "10000",
+                "name": "FIRST",
+                "description": "First Project Category"
+            }
+        },
+        "comment": [
+            {
+                "self": "http://www.example.com/jira/rest/api/2/issue/10010/comment/10000",
+                "id": "10000",
+                "author": {
+                    "self": "http://www.example.com/jira/rest/api/2/user?username=fred",
+                    "name": "fred",
+                    "displayName": "Fred F. User",
+                    "active": false
+                },
+                "body": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque eget venenatis elit. Duis eu justo eget augue iaculis fermentum. Sed semper quam laoreet nisi egestas at posuere augue semper.",
+                "updateAuthor": {
+                    "self": "http://www.example.com/jira/rest/api/2/user?username=fred",
+                    "name": "fred",
+                    "displayName": "Fred F. User",
+                    "active": false
+                },
+                "created": "2017-03-08T03:29:43.383+0000",
+                "updated": "2017-03-08T03:29:43.383+0000",
+                "visibility": {
+                    "type": "role",
+                    "value": "Administrators"
+                }
+            }
+        ],
+        "issuelinks": [
+            {
+                "id": "10001",
+                "type": {
+                    "id": "10000",
+                    "name": "Dependent",
+                    "inward": "depends on",
+                    "outward": "is depended by"
+                },
+                "outwardIssue": {
+                    "id": "10004L",
+                    "key": "PRJ-2",
+                    "self": "http://www.example.com/jira/rest/api/2/issue/PRJ-2",
+                    "fields": {
+                        "status": {
+                            "iconUrl": "http://www.example.com/jira//images/icons/statuses/open.png",
+                            "name": "Open"
+                        }
+                    }
+                }
+            },
+            {
+                "id": "10002",
+                "type": {
+                    "id": "10000",
+                    "name": "Dependent",
+                    "inward": "depends on",
+                    "outward": "is depended by"
+                },
+                "inwardIssue": {
+                    "id": "10004",
+                    "key": "PRJ-3",
+                    "self": "http://www.example.com/jira/rest/api/2/issue/PRJ-3",
+                    "fields": {
+                        "status": {
+                            "iconUrl": "http://www.example.com/jira//images/icons/statuses/open.png",
+                            "name": "Open"
+                        }
+                    }
+                }
+            }
+        ],
+        "worklog": [
+            {
+                "self": "http://www.example.com/jira/rest/api/2/issue/10010/worklog/10000",
+                "author": {
+                    "self": "http://www.example.com/jira/rest/api/2/user?username=fred",
+                    "name": "fred",
+                    "displayName": "Fred F. User",
+                    "active": false
+                },
+                "updateAuthor": {
+                    "self": "http://www.example.com/jira/rest/api/2/user?username=fred",
+                    "name": "fred",
+                    "displayName": "Fred F. User",
+                    "active": false
+                },
+                "comment": "I did some work here.",
+                "updated": "2017-03-08T03:29:43.684+0000",
+                "visibility": {
+                    "type": "group",
+                    "value": "jira-developers"
+                },
+                "started": "2017-03-08T03:29:43.684+0000",
+                "timeSpent": "3h20m",
+                "timeSpentSeconds": 12000,
+                "id": "100028",
+                "issueId": "10002"
+            }
+        ],
+        "updated": 1,
+        "timetracking": {
+            "originalEstimate": "10m",
+            "remainingEstimate": "3m",
+            "timeSpent": "6m",
+            "originalEstimateSeconds": 600,
+            "remainingEstimateSeconds": 200,
+            "timeSpentSeconds": 400
+        }
+    }
+}
blob - /dev/null
blob + b83bcf3e4b98697cc1693190dc78ba336010b40a (mode 644)
--- /dev/null
+++ jira/jira.go
@@ -0,0 +1,84 @@
+//
+package main
+
+import (
+	"encoding/json"
+	"fmt"
+	"time"
+)
+
+type Issue struct {
+	ID          string `json:"id"` // TODO(otl): int?
+	URL         string `json:"self"`
+	Key         string `json:"key"`
+	Description string
+	Project     Project
+	Comments    []Comment
+}
+
+type Project struct {
+	ID   string `json:"id"` // TODO(otl): int?
+	Name string `json:"name"`
+	URL  string `json:"self"`
+}
+
+type Comment struct {
+	URL          string    `json:"self"`
+	ID           string    `json:"id"` // TODO(otl): int?
+	Body         string    `json:"body"`
+	Created      time.Time `json:"created"`
+	Updated      time.Time `json:"updated"`
+	Author       User      `json:"author"`
+	UpdateAuthor User      `json:"updateAuthor"`
+}
+
+func (c *Comment) UnmarshalJSON(b []byte) error {
+	type alias Comment
+	aux := &struct {
+		Created string `json:"created"`
+		Updated string `json:"updated"`
+		*alias
+	}{
+		alias: (*alias)(c),
+	}
+	if err := json.Unmarshal(b, aux); err != nil {
+		return err
+	}
+	var err error
+	tstamp := "2006-01-02T15:04:05.999-0700"
+	c.Created, err = time.Parse(tstamp, aux.Created)
+	if err != nil {
+		return fmt.Errorf("parse created time: %w", err)
+	}
+	c.Updated, err = time.Parse(tstamp, aux.Updated)
+	if err != nil {
+		return fmt.Errorf("parse updated time: %w", err)
+	}
+	return nil
+}
+
+type User struct {
+	Name        string `json:"name"`
+	DisplayName string `json:"displayName"`
+}
+
+func (issue *Issue) UnmarshalJSON(b []byte) error {
+	type alias Issue
+	aux := &struct {
+		Fields struct {
+			Description string
+			Comment     []Comment
+			Project     Project
+		}
+		*alias
+	}{
+		alias: (*alias)(issue),
+	}
+	if err := json.Unmarshal(b, aux); err != nil {
+		return err
+	}
+	issue.Comments = aux.Fields.Comment
+	issue.Description = aux.Fields.Description
+	issue.Project = aux.Fields.Project
+	return nil
+}
blob - /dev/null
blob + 7b7992b9e2273f94ef78eb36746b8d7bc5e7d6e2 (mode 644)
--- /dev/null
+++ jira/jira_test.go
@@ -0,0 +1,32 @@
+package main
+
+import (
+	"encoding/json"
+	"os"
+	"testing"
+)
+
+func TestIssue(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)
+	}
+}
+
+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 - /dev/null
blob + b8598ddab249a4c773596c165ff8186cd28c2ab6 (mode 644)
--- /dev/null
+++ jira/print.go
@@ -0,0 +1,35 @@
+package main
+
+import (
+	"fmt"
+	"io"
+	"strings"
+)
+
+func printIssue(w io.Writer, i *Issue) (n int, err error) {
+	buf := &strings.Builder{}
+	fmt.Fprintln(buf, "URL:", i.URL)
+	fmt.Fprintln(buf)
+
+	fmt.Fprintln(buf, i.Description)
+	fmt.Fprintln(buf)
+
+	for _, c := range i.Comments {
+		buf.WriteString("\t")
+		printComment(buf, &c)
+	}
+	return w.Write([]byte(buf.String()))
+}
+
+func printComment(w io.Writer, c *Comment) (n int, err error) {
+	buf := &strings.Builder{}
+	date := c.Created
+	if !c.Updated.IsZero() {
+		date = c.Updated
+	}
+	fmt.Fprintln(buf, "Date:", date)
+	fmt.Fprintln(buf, "From:", c.Author.Name)
+	fmt.Fprintln(buf)
+	fmt.Fprintln(buf, c.Body)
+	return w.Write([]byte(buf.String()))
+}
blob - /dev/null
blob + 30254dd4d27efe5f9765de36b1994068c770b813 (mode 644)
--- /dev/null
+++ jira/project.json
@@ -0,0 +1,116 @@
+{
+    "expand": "description,lead,issueTypes,url,projectKeys",
+    "self": "http://www.example.com/jira/rest/api/2/project/EX",
+    "id": "10000",
+    "key": "EX",
+    "description": "This project was created as an example for REST.",
+    "lead": {
+        "self": "http://www.example.com/jira/rest/api/2/user?username=fred",
+        "key": "fred",
+        "accountId": "99:27935d01-92a7-4687-8272-a9b8d3b2ae2e",
+        "name": "fred",
+        "avatarUrls": {
+            "48x48": "http://www.example.com/jira/secure/useravatar?size=large&ownerId=fred",
+            "24x24": "http://www.example.com/jira/secure/useravatar?size=small&ownerId=fred",
+            "16x16": "http://www.example.com/jira/secure/useravatar?size=xsmall&ownerId=fred",
+            "32x32": "http://www.example.com/jira/secure/useravatar?size=medium&ownerId=fred"
+        },
+        "displayName": "Fred F. User",
+        "active": false
+    },
+    "components": [
+        {
+            "self": "http://www.example.com/jira/rest/api/2/component/10000",
+            "id": "10000",
+            "name": "Component 1",
+            "description": "This is a JIRA component",
+            "lead": {
+                "self": "http://www.example.com/jira/rest/api/2/user?username=fred",
+                "key": "fred",
+                "accountId": "99:27935d01-92a7-4687-8272-a9b8d3b2ae2e",
+                "name": "fred",
+                "avatarUrls": {
+                    "48x48": "http://www.example.com/jira/secure/useravatar?size=large&ownerId=fred",
+                    "24x24": "http://www.example.com/jira/secure/useravatar?size=small&ownerId=fred",
+                    "16x16": "http://www.example.com/jira/secure/useravatar?size=xsmall&ownerId=fred",
+                    "32x32": "http://www.example.com/jira/secure/useravatar?size=medium&ownerId=fred"
+                },
+                "displayName": "Fred F.User",
+                "active": false
+            },
+            "assigneeType": "PROJECT_LEAD",
+            "assignee": {
+                "self": "http://www.example.com/jira/rest/api/2/user?username=fred",
+                "key": "fred",
+                "accountId": "99:27935d01-92a7-4687-8272-a9b8d3b2ae2e",
+                "name": "fred",
+                "avatarUrls": {
+                    "48x48": "http://www.example.com/jira/secure/useravatar?size=large&ownerId=fred",
+                    "24x24": "http://www.example.com/jira/secure/useravatar?size=small&ownerId=fred",
+                    "16x16": "http://www.example.com/jira/secure/useravatar?size=xsmall&ownerId=fred",
+                    "32x32": "http://www.example.com/jira/secure/useravatar?size=medium&ownerId=fred"
+                },
+                "displayName": "Fred F. User",
+                "active": false
+            },
+            "realAssigneeType": "PROJECT_LEAD",
+            "realAssignee": {
+                "self": "http://www.example.com/jira/rest/api/2/user?username=fred",
+                "key": "fred",
+                "accountId": "99:27935d01-92a7-4687-8272-a9b8d3b2ae2e",
+                "name": "fred",
+                "avatarUrls": {
+                    "48x48": "http://www.example.com/jira/secure/useravatar?size=large&ownerId=fred",
+                    "24x24": "http://www.example.com/jira/secure/useravatar?size=small&ownerId=fred",
+                    "16x16": "http://www.example.com/jira/secure/useravatar?size=xsmall&ownerId=fred",
+                    "32x32": "http://www.example.com/jira/secure/useravatar?size=medium&ownerId=fred"
+                },
+                "displayName": "Fred F. User",
+                "active": false
+            },
+            "isAssigneeTypeValid": false,
+            "project": "HSP",
+            "projectId": 10000
+        }
+    ],
+    "issueTypes": [
+        {
+            "self": "http://localhost:8090/jira/rest/api/2.0/issueType/3",
+            "id": "3",
+            "description": "A task that needs to be done.",
+            "iconUrl": "http://localhost:8090/jira/images/icons/issuetypes/task.png",
+            "name": "Task",
+            "subtask": false,
+            "avatarId": 1
+        },
+        {
+            "self": "http://localhost:8090/jira/rest/api/2.0/issueType/1",
+            "id": "1",
+            "description": "A problem with the software.",
+            "iconUrl": "http://localhost:8090/jira/images/icons/issuetypes/bug.png",
+            "name": "Bug",
+            "subtask": false,
+            "avatarId": 10002
+        }
+    ],
+    "url": "http://www.example.com/jira/browse/EX",
+    "email": "from-jira@example.com",
+    "assigneeType": "PROJECT_LEAD",
+    "versions": [],
+    "name": "Example",
+    "roles": {
+        "Developers": "http://www.example.com/jira/rest/api/2/project/EX/role/10000"
+    },
+    "avatarUrls": {
+        "48x48": "http://www.example.com/jira/secure/projectavatar?size=large&pid=10000",
+        "24x24": "http://www.example.com/jira/secure/projectavatar?size=small&pid=10000",
+        "16x16": "http://www.example.com/jira/secure/projectavatar?size=xsmall&pid=10000",
+        "32x32": "http://www.example.com/jira/secure/projectavatar?size=medium&pid=10000"
+    },
+    "projectCategory": {
+        "self": "http://www.example.com/jira/rest/api/2/projectCategory/10000",
+        "id": "10000",
+        "name": "FIRST",
+        "description": "First Project Category"
+    }
+}