From 4b8abfb28cfbade4304d398ab3168ba7ae744f30 Mon Sep 17 00:00:00 2001 From: Chris Cummer Date: Wed, 11 Jul 2018 17:43:54 -0700 Subject: [PATCH] Add interactive Jira documentation --- _site/content/posts/modules/jira.md | 15 +- docs/index.xml | 8 +- docs/posts/index.xml | 8 +- docs/posts/modules/jira/index.html | 15 +- .../github.com/darkSasori/todoist/.gitignore | 2 + .../github.com/darkSasori/todoist/.travis.yml | 19 +++ .../github.com/darkSasori/todoist/Gopkg.lock | 21 +++ .../github.com/darkSasori/todoist/Gopkg.toml | 34 ++++ vendor/github.com/darkSasori/todoist/LICENSE | 21 +++ .../github.com/darkSasori/todoist/README.md | 6 + .../github.com/darkSasori/todoist/projects.go | 149 +++++++++++++++++ vendor/github.com/darkSasori/todoist/task.go | 158 ++++++++++++++++++ .../github.com/darkSasori/todoist/todoist.go | 145 ++++++++++++++++ 13 files changed, 593 insertions(+), 8 deletions(-) create mode 100644 vendor/github.com/darkSasori/todoist/.gitignore create mode 100644 vendor/github.com/darkSasori/todoist/.travis.yml create mode 100644 vendor/github.com/darkSasori/todoist/Gopkg.lock create mode 100644 vendor/github.com/darkSasori/todoist/Gopkg.toml create mode 100644 vendor/github.com/darkSasori/todoist/LICENSE create mode 100644 vendor/github.com/darkSasori/todoist/README.md create mode 100644 vendor/github.com/darkSasori/todoist/projects.go create mode 100644 vendor/github.com/darkSasori/todoist/task.go create mode 100644 vendor/github.com/darkSasori/todoist/todoist.go diff --git a/_site/content/posts/modules/jira.md b/_site/content/posts/modules/jira.md index 3daf62b7..081f4a31 100644 --- a/_site/content/posts/modules/jira.md +++ b/_site/content/posts/modules/jira.md @@ -21,7 +21,20 @@ wtf/jira/ ## Keyboard Commands -None. +Key: `[return]`
+Action: Open the selected issue in the browser. + +Key: `j`
+Action: Select the next item in the list. + +Key: `k`
+Action: Select the previous item in the list. + +Key: `↓`
+Action: Select the next item in the list. + +Key: `↑`
+Action: Select the previous item in the list. ## Configuration diff --git a/docs/index.xml b/docs/index.xml index b2e1c6e1..8088fa69 100644 --- a/docs/index.xml +++ b/docs/index.xml @@ -310,9 +310,11 @@ Key: j Action: Select the next item in the list. https://wtfutil.com/posts/modules/jira/ Displays all Jira issues assigned to you for the specified project. Source Code wtf/jira/ Required ENV Variables Key: WTF_JIRA_API_KEY Value: Your Jira API key. -Keyboard Commands None. -Configuration Single Jira Project jira:colors:rows:even:"lightblue"odd:"white"domain:"https://umbrellacorp.atlassian.net"email:"chriscummer@me.com"enabled:truejql:"issueType = Story"position:top:4left:1height:1width:2project:"ProjectA"refreshInterval:900username:"chris.cummer"verifyServerCertificate:true Multiple Jira Projects If you want to monitor multiple Jira projects, use the following configuration (note the difference in project): -jira:colors:rows:even:"lightblue"odd:"white"domain:"https://umbrellacorp.atlassian.net"email:"chriscummer@me.com"enabled:truejql:"issueType = Story"position:top:4left:1height:1width:2project:["ProjectA","ProjectB"]refreshInterval:900username:"chris.cummer"verifyServerCertificate:true Attributes colors.rows.even Define the foreground color for even-numbered rows. Values: Any X11 color name. +Keyboard Commands Key: [return] Action: Open the selected issue in the browser. +Key: j Action: Select the next item in the list. +Key: k Action: Select the previous item in the list. +Key: ↓ Action: Select the next item in the list. +Key: ↑ Action: Select the previous item in the list. diff --git a/docs/posts/index.xml b/docs/posts/index.xml index 1ca92370..b3ca1a6f 100644 --- a/docs/posts/index.xml +++ b/docs/posts/index.xml @@ -310,9 +310,11 @@ Key: j Action: Select the next item in the list. https://wtfutil.com/posts/modules/jira/ Displays all Jira issues assigned to you for the specified project. Source Code wtf/jira/ Required ENV Variables Key: WTF_JIRA_API_KEY Value: Your Jira API key. -Keyboard Commands None. -Configuration Single Jira Project jira:colors:rows:even:"lightblue"odd:"white"domain:"https://umbrellacorp.atlassian.net"email:"chriscummer@me.com"enabled:truejql:"issueType = Story"position:top:4left:1height:1width:2project:"ProjectA"refreshInterval:900username:"chris.cummer"verifyServerCertificate:true Multiple Jira Projects If you want to monitor multiple Jira projects, use the following configuration (note the difference in project): -jira:colors:rows:even:"lightblue"odd:"white"domain:"https://umbrellacorp.atlassian.net"email:"chriscummer@me.com"enabled:truejql:"issueType = Story"position:top:4left:1height:1width:2project:["ProjectA","ProjectB"]refreshInterval:900username:"chris.cummer"verifyServerCertificate:true Attributes colors.rows.even Define the foreground color for even-numbered rows. Values: Any X11 color name. +Keyboard Commands Key: [return] Action: Open the selected issue in the browser. +Key: j Action: Select the next item in the list. +Key: k Action: Select the previous item in the list. +Key: ↓ Action: Select the next item in the list. +Key: ↑ Action: Select the previous item in the list. diff --git a/docs/posts/modules/jira/index.html b/docs/posts/modules/jira/index.html index edcfcad4..7e14006c 100644 --- a/docs/posts/modules/jira/index.html +++ b/docs/posts/modules/jira/index.html @@ -148,7 +148,20 @@ height="0" width="0" style="display:none;visibility:hidden">

Keyboard Commands

-

None.

+

Key: [return]
+Action: Open the selected issue in the browser.

+ +

Key: j
+Action: Select the next item in the list.

+ +

Key: k
+Action: Select the previous item in the list.

+ +

Key:
+Action: Select the next item in the list.

+ +

Key:
+Action: Select the previous item in the list.

Configuration

diff --git a/vendor/github.com/darkSasori/todoist/.gitignore b/vendor/github.com/darkSasori/todoist/.gitignore new file mode 100644 index 00000000..9a990fe4 --- /dev/null +++ b/vendor/github.com/darkSasori/todoist/.gitignore @@ -0,0 +1,2 @@ +*.out +vendor/* diff --git a/vendor/github.com/darkSasori/todoist/.travis.yml b/vendor/github.com/darkSasori/todoist/.travis.yml new file mode 100644 index 00000000..0052d33c --- /dev/null +++ b/vendor/github.com/darkSasori/todoist/.travis.yml @@ -0,0 +1,19 @@ +language: go + +notifications: + email: false + +go: + - "1.10.3" + - master + +before_install: + - curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh + - go get -u github.com/golang/lint/golint + +install: + - dep ensure + +script: + - golint -set_exit_status + - go test -v diff --git a/vendor/github.com/darkSasori/todoist/Gopkg.lock b/vendor/github.com/darkSasori/todoist/Gopkg.lock new file mode 100644 index 00000000..99656b15 --- /dev/null +++ b/vendor/github.com/darkSasori/todoist/Gopkg.lock @@ -0,0 +1,21 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + branch = "master" + name = "github.com/darkSasori/todoist" + packages = ["."] + revision = "ab3a9f0b9d551ab4e57f1caae9bb20d5aa51ec10" + +[[projects]] + branch = "v1" + name = "gopkg.in/jarcoal/httpmock.v1" + packages = ["."] + revision = "16f9a43967d613f0adc2000f0094a17b9f6c4c20" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "4d0d4d6dd7b4141be119cb48281ea7c10f05d3037c0e4ac49560cf4af21917a7" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/vendor/github.com/darkSasori/todoist/Gopkg.toml b/vendor/github.com/darkSasori/todoist/Gopkg.toml new file mode 100644 index 00000000..ec866dff --- /dev/null +++ b/vendor/github.com/darkSasori/todoist/Gopkg.toml @@ -0,0 +1,34 @@ +# Gopkg.toml example +# +# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" +# +# [prune] +# non-go = false +# go-tests = true +# unused-packages = true + + +[[constraint]] + branch = "v1" + name = "gopkg.in/jarcoal/httpmock.v1" + +[prune] + go-tests = true + unused-packages = true diff --git a/vendor/github.com/darkSasori/todoist/LICENSE b/vendor/github.com/darkSasori/todoist/LICENSE new file mode 100644 index 00000000..cc68e07a --- /dev/null +++ b/vendor/github.com/darkSasori/todoist/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Lineu Felipe + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/darkSasori/todoist/README.md b/vendor/github.com/darkSasori/todoist/README.md new file mode 100644 index 00000000..8ffc3976 --- /dev/null +++ b/vendor/github.com/darkSasori/todoist/README.md @@ -0,0 +1,6 @@ +# todoist +[![godoc](https://godoc.org/github.com/darkSasori/todoist?status.svg)](https://godoc.org/github.com/darkSasori/todoist) +[![Build Status](https://travis-ci.org/darkSasori/todoist.svg?branch=master)](https://travis-ci.org/darkSasori/todoist) +[![Go Report Card](https://goreportcard.com/badge/github.com/darkSasori/todoist)](https://goreportcard.com/report/github.com/darkSasori/todoist) + +Unofficial todoist api implementation diff --git a/vendor/github.com/darkSasori/todoist/projects.go b/vendor/github.com/darkSasori/todoist/projects.go new file mode 100644 index 00000000..9493b18a --- /dev/null +++ b/vendor/github.com/darkSasori/todoist/projects.go @@ -0,0 +1,149 @@ +package todoist + +import ( + "encoding/json" + "fmt" + "io" + "net/http" +) + +// Project is a model of todoist project entity +type Project struct { + ID int `json:"id"` + Name string `json:"name"` + CommentCount int `json:"comment_count"` + Order int `json:"order"` + Indent int `json:"indent"` +} + +func decodeProject(body io.ReadCloser) (Project, error) { + defer body.Close() + decoder := json.NewDecoder(body) + var project Project + + if err := decoder.Decode(&project); err != nil { + return Project{}, err + } + return project, nil +} + +// ListProject return all projects +// +// Example: +// todoist.Token = "your token" +// projects, err := todoist.ListProject() +// if err != nil { +// panic(err) +// } +// fmt.Println(projects) +func ListProject() ([]Project, error) { + res, err := makeRequest(http.MethodGet, "projects", nil) + if err != nil { + return []Project{}, err + } + + defer res.Body.Close() + decoder := json.NewDecoder(res.Body) + var projects []Project + + if err := decoder.Decode(&projects); err != nil { + return []Project{}, err + } + + return projects, nil +} + +// GetProject return a project by id +// +// Example: +// todoist.Token = "your token" +// project, err := todoist.GetProject(1) +// if err != nil { +// panic(err) +// } +// fmt.Println(project) +func GetProject(id int) (Project, error) { + path := fmt.Sprintf("projects/%d", id) + res, err := makeRequest(http.MethodGet, path, nil) + if err != nil { + return Project{}, err + } + + return decodeProject(res.Body) +} + +// CreateProject create a new project with a name +// +// Example: +// todoist.Token = "your token" +// project, err := todoist.CreateProject("New Project") +// if err != nil { +// panic(err) +// } +// fmt.Println(project) +func CreateProject(name string) (Project, error) { + project := struct { + Name string `json:"name"` + }{ + name, + } + + res, err := makeRequest(http.MethodPost, "projects", project) + if err != nil { + return Project{}, err + } + + return decodeProject(res.Body) +} + +// Delete project +// +// Example: +// todoist.Token = "your token" +// project, err := todoist.GetProject(1) +// if err != nil { +// panic(err) +// } +// err = project.Delete() +// if err != nil { +// panic(err) +// } +func (p Project) Delete() error { + path := fmt.Sprintf("projects/%d", p.ID) + _, err := makeRequest(http.MethodDelete, path, nil) + if err != nil { + return err + } + + return nil +} + +// Update project +// +// Example: +// todoist.Token = "your token" +// project, err := todoist.GetProject(1) +// if err != nil { +// panic(err) +// } +// project.Name = "updated" +// err = project.Update() +// if err != nil { +// panic(err) +// } +// fmt.Println(project) +func (p Project) Update() error { + path := fmt.Sprintf("projects/%d", p.ID) + project := struct { + Name string `json:"name"` + }{ + p.Name, + } + + _, err := makeRequest(http.MethodPost, path, project) + if err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/darkSasori/todoist/task.go b/vendor/github.com/darkSasori/todoist/task.go new file mode 100644 index 00000000..f2c0ce06 --- /dev/null +++ b/vendor/github.com/darkSasori/todoist/task.go @@ -0,0 +1,158 @@ +package todoist + +import ( + "encoding/json" + "fmt" + "io" + "net/http" +) + +// Task is a model of todoist project entity +type Task struct { + ID int `json:"id"` + CommentCount int `json:"comment_count"` + Completed bool `json:"completed"` + Content string `json:"content"` + Indent int `json:"indent"` + LabelIDs []int `json:"label_ids"` + Order int `json:"order"` + Priority int `json:"priority"` + ProjectID int `json:"project_id"` + Due Due `json:"due"` +} + +// Due is a model of todoist project entity +type Due struct { + String string `json:"string"` + Date string `json:"date"` + Datetime CustomTime `json:"datetime"` + Timezone string `json:"timezone"` +} + +func (t Task) taskSave() taskSave { + return taskSave{ + t.Content, + t.ProjectID, + t.Order, + t.LabelIDs, + t.Priority, + t.Due.String, + t.Due.Datetime, + "en", + } +} + +func decodeTask(body io.ReadCloser) (Task, error) { + defer body.Close() + decoder := json.NewDecoder(body) + var task Task + + if err := decoder.Decode(&task); err != nil { + return Task{}, err + } + return task, nil +} + +// QueryParam is a map[string]string to build http query +type QueryParam map[string]string + +func (qp QueryParam) String() string { + if len(qp) == 0 { + return "" + } + + ret := "?" + for key, value := range qp { + if ret != "?" { + ret = ret + "&" + } + ret = ret + key + "=" + value + } + + return ret +} + +// ListTask return all task, you can filter using QueryParam +// See documentation: https://developer.todoist.com/rest/v8/#get-tasks +func ListTask(qp QueryParam) ([]Task, error) { + path := fmt.Sprintf("tasks%s", qp) + res, err := makeRequest(http.MethodGet, path, nil) + if err != nil { + return []Task{}, err + } + + defer res.Body.Close() + decoder := json.NewDecoder(res.Body) + var tasks []Task + + if err := decoder.Decode(&tasks); err != nil { + return []Task{}, err + } + + return tasks, nil +} + +// GetTask return a task by id +func GetTask(id int) (Task, error) { + path := fmt.Sprintf("tasks/%d", id) + res, err := makeRequest(http.MethodGet, path, nil) + if err != nil { + return Task{}, err + } + + return decodeTask(res.Body) +} + +// CreateTask create a new task +func CreateTask(task Task) (Task, error) { + res, err := makeRequest(http.MethodPost, "tasks", task.taskSave()) + if err != nil { + return Task{}, err + } + + return decodeTask(res.Body) +} + +// Delete remove a task +func (t Task) Delete() error { + path := fmt.Sprintf("tasks/%d", t.ID) + _, err := makeRequest(http.MethodDelete, path, nil) + if err != nil { + return err + } + + return nil +} + +// Update a task +func (t Task) Update() error { + path := fmt.Sprintf("tasks/%d", t.ID) + _, err := makeRequest(http.MethodPost, path, t.taskSave()) + if err != nil { + return err + } + + return nil +} + +// Close mask task as done +func (t Task) Close() error { + path := fmt.Sprintf("tasks/%d/close", t.ID) + _, err := makeRequest(http.MethodPost, path, nil) + if err != nil { + return err + } + + return nil +} + +// Reopen a task +func (t Task) Reopen() error { + path := fmt.Sprintf("tasks/%d/reopen", t.ID) + _, err := makeRequest(http.MethodPost, path, nil) + if err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/darkSasori/todoist/todoist.go b/vendor/github.com/darkSasori/todoist/todoist.go new file mode 100644 index 00000000..d487426c --- /dev/null +++ b/vendor/github.com/darkSasori/todoist/todoist.go @@ -0,0 +1,145 @@ +package todoist + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "strings" + "time" +) + +// Token save the personal token from todoist +var Token string +var todoistURL = "https://beta.todoist.com/API/v8/" + +func makeRequest(method, endpoint string, data interface{}) (*http.Response, error) { + url := todoistURL + endpoint + body := bytes.NewBuffer([]byte{}) + + if data != nil { + json, err := json.Marshal(data) + if err != nil { + return nil, err + } + body = bytes.NewBuffer(json) + } + + req, err := http.NewRequest(method, url, body) + if err != nil { + return nil, err + } + + bearer := fmt.Sprintf("Bearer %s", Token) + req.Header.Add("Authorization", bearer) + + if data != nil { + req.Header.Add("Content-Type", "application/json") + } + + client := &http.Client{} + res, err := client.Do(req) + if err != nil { + return nil, err + } + + if res.StatusCode >= 400 { + defer res.Body.Close() + str, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil, err + } + return nil, fmt.Errorf(string(str)) + } + + return res, nil +} + +const ctLayout = "2006-01-02T15:04:05+00:00" + +// CustomTime had a custom json date format +type CustomTime struct { + time.Time +} + +// UnmarshalJSON convert from []byte to CustomTime +func (ct *CustomTime) UnmarshalJSON(b []byte) (err error) { + s := strings.Trim(string(b), "\"") + if s == "null" { + ct.Time = time.Time{} + return nil + } + + ct.Time, err = time.Parse(ctLayout, s) + return err +} + +// MarshalJSON convert CustomTime to []byte +func (ct CustomTime) MarshalJSON() ([]byte, error) { + if ct.Time.IsZero() { + return []byte("null"), nil + } + return []byte(`"` + ct.Time.Format(ctLayout) + `"`), nil +} + +type taskSave struct { + Content string `json:"content"` + ProjectID int `json:"project_id,omitempty"` + Order int `json:"order,omitempty"` + LabelIDs []int `json:"label_ids,omitempty"` + Priority int `json:"priority,omitempty"` + DueString string `json:"due_string,omitempty"` + DueDateTime CustomTime `json:"due_datetime,omitempty"` + DueLang string `json:"due_lang,omitempty"` +} + +func (ts taskSave) MarshalJSON() ([]byte, error) { + buffer := bytes.NewBufferString("{") + + if ts.Content == "" { + return nil, fmt.Errorf("Content is empty") + } + buffer.WriteString(fmt.Sprintf("\"content\":\"%s\"", ts.Content)) + + if ts.ProjectID != 0 { + buffer.WriteString(fmt.Sprintf(",\"project_id\":%d", ts.ProjectID)) + } + + if ts.Order != 0 { + buffer.WriteString(fmt.Sprintf(",\"order\":%d", ts.Order)) + } + + if !ts.DueDateTime.IsZero() { + buffer.WriteString(",\"due_datetime\":") + json, err := json.Marshal(ts.DueDateTime) + if err != nil { + return nil, err + } + buffer.Write(json) + } + + if len(ts.LabelIDs) != 0 { + buffer.WriteString(",\"label_ids\":") + json, err := json.Marshal(ts.LabelIDs) + if err != nil { + return nil, err + } + buffer.Write(json) + } + + if ts.Priority != 0 { + buffer.WriteString(fmt.Sprintf(",\"priority\":%d", ts.Priority)) + } + + if ts.DueString != "" { + buffer.WriteString(fmt.Sprintf(",\"due_string\":\"%s\"", ts.DueString)) + } + + if ts.DueLang != "" { + buffer.WriteString(fmt.Sprintf(",\"due_lang\":\"%s\"", ts.DueLang)) + } + + buffer.WriteString("}") + return buffer.Bytes(), nil +}