Compare commits
No commits in common. 'f02cbaebf7b5a48175649a3c78834cf04a87f6c7' and 'cd3ca50a2285861e913de520bcad1b268d58c83b' have entirely different histories.
f02cbaebf7
...
cd3ca50a22
7 changed files with 52 additions and 228 deletions
@ -1,45 +1,21 @@ |
|||||||
#+TITLE: Worklog |
* Worklog |
||||||
#+AUTHOR: Riyyi |
|
||||||
#+LANGUAGE: en |
|
||||||
#+OPTIONS: toc:nil |
|
||||||
|
|
||||||
Register worklog entries to the Jira API. |
Register worklog entries to the Jira API. |
||||||
|
|
||||||
** Getting started |
* Download |
||||||
|
|
||||||
*** Clone |
** Clone |
||||||
|
|
||||||
#+BEGIN_SRC sh |
#+BEGIN_SRC sh |
||||||
$ git clone https://github.com/riyyi/worklog |
$ git clone https://github.com/riyyi/worklog |
||||||
#+END_SRC |
#+END_SRC |
||||||
|
|
||||||
*** Build instructions |
* Build instructions |
||||||
|
|
||||||
#+BEGIN_SRC sh |
#+BEGIN_SRC sh |
||||||
$ go build |
$ go build |
||||||
#+END_SRC |
#+END_SRC |
||||||
|
|
||||||
*** Usage |
* Gitignore |
||||||
|
|
||||||
#+BEGIN_SRC sh |
git update-index --assume-unchanged src/secrets.go |
||||||
$ worklog --help |
|
||||||
worklog - process a worklog file |
|
||||||
|
|
||||||
Usage: worklog [--decl MONTH] [--process] [--issues] FILE |
|
||||||
|
|
||||||
Positional arguments: |
|
||||||
FILE the file to perform the action on |
|
||||||
|
|
||||||
Options: |
|
||||||
--decl MONTH, -d MONTH |
|
||||||
Generate travel declaration table |
|
||||||
--process, -p Process specified file and call Jira API |
|
||||||
--issues, -i Store issues in specified file |
|
||||||
--help, -h display this help and exit |
|
||||||
#+END_SRC |
|
||||||
|
|
||||||
** Gitignore |
|
||||||
|
|
||||||
#+BEGIN_SRC sh |
|
||||||
$ git update-index --assume-unchanged src/secrets.go |
|
||||||
#+END_SRC |
|
||||||
|
@ -1,135 +0,0 @@ |
|||||||
package src |
|
||||||
|
|
||||||
import ( |
|
||||||
"bufio" |
|
||||||
"encoding/json" |
|
||||||
"fmt" |
|
||||||
"os" |
|
||||||
"path/filepath" |
|
||||||
) |
|
||||||
|
|
||||||
type IssueData struct { |
|
||||||
totalIssues []issueResponse |
|
||||||
} |
|
||||||
|
|
||||||
// Constructor
|
|
||||||
func MakeIssueData() IssueData { |
|
||||||
return IssueData {} |
|
||||||
} |
|
||||||
|
|
||||||
func (self *IssueData) GenerateIssuesFile(path string) { |
|
||||||
// Safety check
|
|
||||||
verify(filepath.Base(path) != "worklog.org", "protected from overwriting worklog file!") |
|
||||||
|
|
||||||
// Get all issues of the active sprint, and its subtasks
|
|
||||||
self.fetchIssues(0) |
|
||||||
|
|
||||||
// Store the issues
|
|
||||||
outputFile, err := os.Create(path) |
|
||||||
assert(err) |
|
||||||
defer outputFile.Close() |
|
||||||
writer := bufio.NewWriter(outputFile) |
|
||||||
defer writer.Flush() |
|
||||||
self.writeIssues(writer) |
|
||||||
|
|
||||||
// Clear the results
|
|
||||||
self.totalIssues = self.totalIssues[:0] |
|
||||||
} |
|
||||||
|
|
||||||
// -----------------------------------------
|
|
||||||
|
|
||||||
type issuesResponse struct { |
|
||||||
StartAt int `json:"startAt"` |
|
||||||
MaxResults int `json:"maxResults"` |
|
||||||
Total int `json:"total"` |
|
||||||
Issues []issueResponse `json:"issues"` |
|
||||||
} |
|
||||||
|
|
||||||
type issueResponse struct { |
|
||||||
Key string `json:"key"` |
|
||||||
Fields fieldsResponse `json:"fields"` |
|
||||||
} |
|
||||||
|
|
||||||
type fieldsResponse struct { |
|
||||||
Summary string `json:"summary"` |
|
||||||
SubTasks []subTaskResponse `json:"subtasks"` |
|
||||||
} |
|
||||||
|
|
||||||
type subTaskResponse struct { |
|
||||||
Key string `json:"key"` |
|
||||||
Fields subTaskFieldsResponse `json:"fields"` |
|
||||||
} |
|
||||||
|
|
||||||
type subTaskFieldsResponse struct { |
|
||||||
Summary string `json:"summary"` |
|
||||||
} |
|
||||||
|
|
||||||
// -----------------------------------------
|
|
||||||
|
|
||||||
func (self *IssueData) fetchIssues(startAt int) error { |
|
||||||
var url string = baseUrl + "/rest/api/2/search" |
|
||||||
var maxResults int = 100 |
|
||||||
data := map[string]interface{}{ |
|
||||||
"fields": []string{ "key", "summary", "subtasks" }, |
|
||||||
"jql": `project = "` + projectName + `" AND sprint IN openSprints() AND issuetype != "Sub-task" ORDER BY created ASC`, |
|
||||||
"maxResults": maxResults, |
|
||||||
"startAt": startAt, |
|
||||||
} |
|
||||||
|
|
||||||
body, err := Request(url, data, 200) // "OK"
|
|
||||||
if err != nil { return err } |
|
||||||
|
|
||||||
var result issuesResponse |
|
||||||
err = json.Unmarshal(body, &result) |
|
||||||
if err != nil { fmt.Println("NOPE!"); return err } |
|
||||||
|
|
||||||
// Add fetched issues
|
|
||||||
self.totalIssues = append(self.totalIssues, result.Issues...) |
|
||||||
|
|
||||||
// Pagination, if more results
|
|
||||||
if startAt + maxResults < result.Total { |
|
||||||
self.fetchIssues(startAt + maxResults) |
|
||||||
} |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self* IssueData) writeIssues(writer *bufio.Writer) { |
|
||||||
// Issues
|
|
||||||
writer.WriteString(".\n") |
|
||||||
var count int = len(self.totalIssues) |
|
||||||
for i, issue := range(self.totalIssues) { |
|
||||||
if i == count - 1 { |
|
||||||
writer.WriteString("└") |
|
||||||
} else { |
|
||||||
writer.WriteString("├") |
|
||||||
} |
|
||||||
writer.WriteString("── ") |
|
||||||
writer.WriteString(issue.Key) |
|
||||||
writer.WriteString(" ") |
|
||||||
writer.WriteString(issue.Fields.Summary) |
|
||||||
writer.WriteString("\n") |
|
||||||
|
|
||||||
// Subtasks
|
|
||||||
var subtaskCount int = len(issue.Fields.SubTasks) |
|
||||||
for j, subtask := range(issue.Fields.SubTasks) { |
|
||||||
// Last issue
|
|
||||||
if i == count - 1 { |
|
||||||
writer.WriteString(" ") |
|
||||||
} else { |
|
||||||
writer.WriteString("│ ") |
|
||||||
} |
|
||||||
// Last subtask
|
|
||||||
if j == subtaskCount - 1 { |
|
||||||
writer.WriteString("└") |
|
||||||
} else { |
|
||||||
writer.WriteString("├") |
|
||||||
} |
|
||||||
writer.WriteString("── ") |
|
||||||
writer.WriteString(subtask.Key) |
|
||||||
writer.WriteString(" ") |
|
||||||
writer.WriteString(subtask.Fields.Summary) |
|
||||||
writer.WriteString("\n") |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,37 +0,0 @@ |
|||||||
package src |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"encoding/base64" |
|
||||||
"encoding/json" |
|
||||||
"fmt" |
|
||||||
"io" |
|
||||||
"net/http" |
|
||||||
) |
|
||||||
|
|
||||||
func Request[T ~map[string]string | ~map[string]interface{}](url string, data T, status int) ([]byte, error) { |
|
||||||
jsonData, err := json.Marshal(data) |
|
||||||
if err != nil { return nil, fmt.Errorf("error marshaling JSON: %s", err) } |
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData)) |
|
||||||
if err != nil { return nil, fmt.Errorf("error creating request: %s", err) } |
|
||||||
|
|
||||||
auth := username + ":" + password |
|
||||||
authHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte(auth)) |
|
||||||
req.Header.Set("Authorization", authHeader) |
|
||||||
req.Header.Set("Content-Type", "application/json") |
|
||||||
|
|
||||||
client := &http.Client{} |
|
||||||
resp, err := client.Do(req) |
|
||||||
if err != nil { return nil, fmt.Errorf("error making request: %s", err) } |
|
||||||
defer resp.Body.Close() |
|
||||||
|
|
||||||
body, err := io.ReadAll(resp.Body) |
|
||||||
if err != nil { return nil, fmt.Errorf("error reading response body: %s", err) } |
|
||||||
|
|
||||||
if resp.StatusCode != status { |
|
||||||
return nil, fmt.Errorf("invalid Jira request:\n%s", string(body)) |
|
||||||
} |
|
||||||
|
|
||||||
return body, nil |
|
||||||
} |
|
Loading…
Reference in new issue