mirror of
https://github.com/interviewstreet/go-jira.git
synced 2025-07-13 01:00:23 +02:00
updating with search and worklogs
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -22,3 +22,4 @@ _testmain.go
|
|||||||
*.exe
|
*.exe
|
||||||
*.test
|
*.test
|
||||||
*.prof
|
*.prof
|
||||||
|
*.iml
|
||||||
|
90
issue.go
90
issue.go
@ -3,6 +3,9 @@ package jira
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -26,6 +29,8 @@ type Issue struct {
|
|||||||
Fields *IssueFields `json:"fields,omitempty"`
|
Fields *IssueFields `json:"fields,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Issues []*Issue
|
||||||
|
|
||||||
// IssueFields represents single fields of a JIRA issue.
|
// IssueFields represents single fields of a JIRA issue.
|
||||||
// Every JIRA issue has several fields attached.
|
// Every JIRA issue has several fields attached.
|
||||||
type IssueFields struct {
|
type IssueFields struct {
|
||||||
@ -34,14 +39,12 @@ type IssueFields struct {
|
|||||||
// * "aggregatetimespent": null,
|
// * "aggregatetimespent": null,
|
||||||
// * "workratio": -1,
|
// * "workratio": -1,
|
||||||
// * "lastViewed": null,
|
// * "lastViewed": null,
|
||||||
// * "labels": [],
|
|
||||||
// * "timeestimate": null,
|
// * "timeestimate": null,
|
||||||
// * "aggregatetimeoriginalestimate": null,
|
// * "aggregatetimeoriginalestimate": null,
|
||||||
// * "timeoriginalestimate": null,
|
// * "timeoriginalestimate": null,
|
||||||
// * "timetracking": {},
|
// * "timetracking": {},
|
||||||
// * "attachment": [],
|
// * "attachment": [],
|
||||||
// * "aggregatetimeestimate": null,
|
// * "aggregatetimeestimate": null,
|
||||||
// * "subtasks": [],
|
|
||||||
// * "environment": null,
|
// * "environment": null,
|
||||||
// * "duedate": null,
|
// * "duedate": null,
|
||||||
Type IssueType `json:"issuetype"`
|
Type IssueType `json:"issuetype"`
|
||||||
@ -61,10 +64,12 @@ type IssueFields struct {
|
|||||||
Status *Status `json:"status,omitempty"`
|
Status *Status `json:"status,omitempty"`
|
||||||
Progress *Progress `json:"progress,omitempty"`
|
Progress *Progress `json:"progress,omitempty"`
|
||||||
AggregateProgress *Progress `json:"aggregateprogress,omitempty"`
|
AggregateProgress *Progress `json:"aggregateprogress,omitempty"`
|
||||||
Worklog []*Worklog `json:"worklog.worklogs,omitempty"`
|
WorklogPage *WorklogPage `json:"worklog,omitempty"`
|
||||||
IssueLinks []*IssueLink `json:"issuelinks,omitempty"`
|
IssueLinks []*IssueLink `json:"issuelinks,omitempty"`
|
||||||
Comments []*Comment `json:"comment.comments,omitempty"`
|
Comments []*Comment `json:"comment.comments,omitempty"`
|
||||||
FixVersions []*FixVersion `json:"fixVersions,omitempty"`
|
FixVersions []*FixVersion `json:"fixVersions,omitempty"`
|
||||||
|
Labels []string `json:"labels,omitempty"`
|
||||||
|
SubTasks Issues `json:"subtasks,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// IssueType represents a type of a JIRA issue.
|
// IssueType represents a type of a JIRA issue.
|
||||||
@ -158,10 +163,39 @@ type Progress struct {
|
|||||||
Total int `json:"total"`
|
Total int `json:"total"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WorklogPage struct {
|
||||||
|
StartAt uint `json:"startAt"`
|
||||||
|
MaxResults uint `json:"maxResults"`
|
||||||
|
Total uint `json:"total"`
|
||||||
|
Worklogs []*Worklog `json:"worklogs"`
|
||||||
|
}
|
||||||
|
|
||||||
// Worklog represents the work log of a JIRA issue.
|
// Worklog represents the work log of a JIRA issue.
|
||||||
// JIRA Wiki: https://confluence.atlassian.com/jira/logging-work-on-an-issue-185729605.html
|
// JIRA Wiki: https://confluence.atlassian.com/jira/logging-work-on-an-issue-185729605.html
|
||||||
type Worklog struct {
|
type Worklog struct {
|
||||||
// TODO Add Worklogs
|
ID string `json:"id"`
|
||||||
|
Self string `json:"self"`
|
||||||
|
IssueId string `json:"issueId"`
|
||||||
|
TimeSpent string `json:"timeSpent"`
|
||||||
|
TimeSpentSeconds uint64 `json:"timeSpentSeconds"`
|
||||||
|
Comment string `json:"comment"`
|
||||||
|
Updated JiraTime `json:"updated"`
|
||||||
|
Created JiraTime `json:"created"`
|
||||||
|
Started JiraTime `json:"started"`
|
||||||
|
Author *Assignee `json:"author"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type JiraTime struct {
|
||||||
|
Time time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *JiraTime) UnmarshalJSON(b []byte) error {
|
||||||
|
ti, err := time.Parse("\"2006-01-02T15:04:05.999-0700\"", string(b))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*t = JiraTime{ti}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IssueLink represents a link between two issues in JIRA.
|
// IssueLink represents a link between two issues in JIRA.
|
||||||
@ -215,6 +249,10 @@ type CommentVisibility struct {
|
|||||||
Value string `json:"value"`
|
Value string `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SearchResult struct {
|
||||||
|
Issues Issues `json:"issues"`
|
||||||
|
}
|
||||||
|
|
||||||
// Get returns a full representation of the issue for the given issue key.
|
// Get returns a full representation of the issue for the given issue key.
|
||||||
// JIRA will attempt to identify the issue by the issueIdOrKey path parameter.
|
// JIRA will attempt to identify the issue by the issueIdOrKey path parameter.
|
||||||
// This can be an issue id, or an issue key.
|
// This can be an issue id, or an issue key.
|
||||||
@ -237,6 +275,38 @@ func (s *IssueService) Get(issueID string) (*Issue, *http.Response, error) {
|
|||||||
return issue, resp, nil
|
return issue, resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CustomFields map[string]string
|
||||||
|
|
||||||
|
// Returns a map of customfield_* keys with string values
|
||||||
|
func (s *IssueService) GetCustomFields(issueID string) (CustomFields, *http.Response, error) {
|
||||||
|
apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s", issueID)
|
||||||
|
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
issue := new(map[string]interface{})
|
||||||
|
resp, err := s.client.Do(req, issue)
|
||||||
|
if err != nil {
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
m := *issue
|
||||||
|
f := m["fields"]
|
||||||
|
cf := make(CustomFields)
|
||||||
|
if f == nil {
|
||||||
|
return cf, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if rec, ok := f.(map[string]interface{}); ok {
|
||||||
|
for key, val := range rec {
|
||||||
|
if strings.Contains(key, "customfield") {
|
||||||
|
cf[key] = fmt.Sprint(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cf, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Create creates an issue or a sub-task from a JSON representation.
|
// Create creates an issue or a sub-task from a JSON representation.
|
||||||
// Creating a sub-task is similar to creating a regular issue, with two important differences:
|
// Creating a sub-task is similar to creating a regular issue, with two important differences:
|
||||||
// The issueType field must correspond to a sub-task issue type and you must provide a parent field in the issue create request containing the id or key of the parent issue.
|
// The issueType field must correspond to a sub-task issue type and you must provide a parent field in the issue create request containing the id or key of the parent issue.
|
||||||
@ -290,3 +360,15 @@ func (s *IssueService) AddLink(issueLink *IssueLink) (*http.Response, error) {
|
|||||||
resp, err := s.client.Do(req, nil)
|
resp, err := s.client.Do(req, nil)
|
||||||
return resp, err
|
return resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Search for tickets
|
||||||
|
// JIRA API docs: https://developer.atlassian.com/jiradev/jira-apis/jira-rest-apis/jira-rest-api-tutorials/jira-rest-api-example-query-issues
|
||||||
|
func (s *IssueService) Search(jql string) (*SearchResult, error) {
|
||||||
|
req, err := s.client.NewRequest("GET", "rest/api/2/search?jql="+url.QueryEscape(jql), nil)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
resp := new(SearchResult)
|
||||||
|
_, err = s.client.Do(req, resp)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user