commit - 72ce55ca9db6e1433ac2c235c5e72c3f2feab374
commit + 8d9dbdfc8a07bca44bcef1a030dc1e248a6ddeaf
blob - /dev/null
blob + be2c7243af97955bd988833827120bab346d0c2b (mode 644)
--- /dev/null
+++ jira/issue.json
+{
+ "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
+//
+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
+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
+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
+{
+ "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"
+ }
+}