1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-07-19 13:19:43 +02:00

Update Jira client library to latest release

Related to #308
This commit is contained in:
HarveyKandola 2019-08-27 17:16:06 +01:00
parent 5b72da037c
commit 0419f3b7b3
18 changed files with 1734 additions and 1274 deletions

6
Gopkg.lock generated
View file

@ -293,12 +293,12 @@
revision = "2caba252f4dc53eaf6b553000885530023f54623" revision = "2caba252f4dc53eaf6b553000885530023f54623"
[[projects]] [[projects]]
digest = "1:ec2b97c119fc66f96b421f8798deb2f87cb4a5ee81cafeaf9b55420d035f8fea" digest = "1:78e84cebc662378af09d7ebaee20b1b813b4b2afcfb91be41d519ce6454eab1d"
name = "gopkg.in/andygrunwald/go-jira.v1" name = "gopkg.in/andygrunwald/go-jira.v1"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "0298784c4606cdf01e99644da115863c052a737c" revision = "7966e7f5ed3e453362a4deac213c0e6cf7563f4c"
version = "v1.5.0" version = "v1.10.0"
[[projects]] [[projects]]
digest = "1:81e1c5cee195fca5de06e2540cb63eea727a850b7e5c213548e7f81521c97a57" digest = "1:81e1c5cee195fca5de06e2540cb63eea727a850b7e5c213548e7f81521c97a57"

File diff suppressed because one or more lines are too long

View file

@ -3,12 +3,11 @@ language: go
sudo: false sudo: false
go: go:
- 1.4 - "1.8.x"
- 1.5 - "1.9.x"
- 1.6 - "1.10.x"
- 1.7 - "1.11.x"
- 1.8 - "1.12.x"
- 1.9
before_install: before_install:
- go get -t ./... - go get -t ./...

41
vendor/gopkg.in/andygrunwald/go-jira.v1/CHANGELOG.md generated vendored Normal file
View file

@ -0,0 +1,41 @@
# [](https://github.com/andygrunwald/go-jira/compare/v1.9.0...v1.10.0) (2019-05-23)
### Bug Fixes
* empty SearchOptions causing malformed request ([b3bf8c2](https://github.com/andygrunwald/go-jira/commit/b3bf8c2))
### Features
* added DeleteAttachment ([e93c0e1](https://github.com/andygrunwald/go-jira/commit/e93c0e1))
# [1.9.0](https://github.com/andygrunwald/go-jira/compare/v1.8.0...v1.9.0) (2019-05-19)
### Features
* **issues:** Added support for AddWorklog and GetWorklogs ([1ebd7e7](https://github.com/andygrunwald/go-jira/commit/1ebd7e7))
# [1.8.0](https://github.com/andygrunwald/go-jira/compare/v1.7.0...v1.8.0) (2019-05-16)
### Bug Fixes
* Add PriorityService to the main ([8491cb0](https://github.com/andygrunwald/go-jira/commit/8491cb0))
### Features
* **filter:** Add GetFavouriteList to FilterService. ([645898e](https://github.com/andygrunwald/go-jira/commit/645898e))
* Add get all priorities ([1c63e25](https://github.com/andygrunwald/go-jira/commit/1c63e25))
* Add ResolutionService to retrieve resolutions ([fb1ce22](https://github.com/andygrunwald/go-jira/commit/fb1ce22))
* Add status category constants ([6223ddd](https://github.com/andygrunwald/go-jira/commit/6223ddd))
* Add StatusCategory GetList ([049a756](https://github.com/andygrunwald/go-jira/commit/049a756))

View file

@ -1,36 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
name = "github.com/fatih/structs"
packages = ["."]
revision = "a720dfa8df582c51dee1b36feabb906bde1588bd"
version = "v1.0"
[[projects]]
branch = "master"
name = "github.com/google/go-querystring"
packages = ["query"]
revision = "53e6ce116135b80d037921a7fdd5138cf32d7a8a"
[[projects]]
name = "github.com/pkg/errors"
packages = ["."]
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
version = "v0.8.0"
[[projects]]
name = "github.com/trivago/tgo"
packages = [
"tcontainer",
"treflect"
]
revision = "e4d1ddd28c17dd89ed26327cf69fded22060671b"
version = "v1.0.1"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "e84ca9eea6d233e0947b0d760913db2983fd4cbf6fd0d8690c737a71affb635c"
solver-name = "gps-cdcl"
solver-version = 1

View file

@ -1,46 +0,0 @@
# 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]]
name = "github.com/fatih/structs"
version = "1.0.0"
[[constraint]]
branch = "master"
name = "github.com/google/go-querystring"
[[constraint]]
name = "github.com/pkg/errors"
version = "0.8.0"
[[constraint]]
name = "github.com/trivago/tgo"
version = "1.0.1"
[prune]
go-tests = true
unused-packages = true

View file

@ -17,15 +17,18 @@
This package is not JIRA API complete (yet), but you can call every API endpoint you want. See [Call a not implemented API endpoint](#call-a-not-implemented-api-endpoint) how to do this. For all possible API endpoints of JIRA have a look at [latest JIRA REST API documentation](https://docs.atlassian.com/jira/REST/latest/). This package is not JIRA API complete (yet), but you can call every API endpoint you want. See [Call a not implemented API endpoint](#call-a-not-implemented-api-endpoint) how to do this. For all possible API endpoints of JIRA have a look at [latest JIRA REST API documentation](https://docs.atlassian.com/jira/REST/latest/).
## Compatible JIRA versions ## Requirements
This package was tested against JIRA v6.3.4 and v7.1.2. * Go >= 1.8
* JIRA v6.3.4 & v7.1.2.
## Installation ## Installation
It is go gettable It is go gettable
$ go get github.com/andygrunwald/go-jira ```bash
go get github.com/andygrunwald/go-jira
```
For stable versions you can use one of our tags with [gopkg.in](http://labix.org/gopkg.in). E.g. For stable versions you can use one of our tags with [gopkg.in](http://labix.org/gopkg.in). E.g.
@ -40,8 +43,10 @@ import (
(optional) to run unit / example tests: (optional) to run unit / example tests:
$ cd $GOPATH/src/github.com/andygrunwald/go-jira ```bash
$ go test -v ./... cd $GOPATH/src/github.com/andygrunwald/go-jira
go test -v ./...
```
## API ## API
@ -89,13 +94,13 @@ For convenience, capability for basic and cookie-based authentication is include
#### Basic auth example #### Basic auth example
A more thorough, [runnable example](examples/basicauth/main.go) is provided in the examples directory. A more thorough, [runnable example](examples/basicauth/main.go) is provided in the examples directory. **It's worth noting that using passwords in basic auth is now deprecated and will be removed.** Jira gives you the ability to [create tokens now.](https://confluence.atlassian.com/cloud/api-tokens-938839638.html)
```go ```go
func main() { func main() {
tp := jira.BasicAuthTransport{ tp := jira.BasicAuthTransport{
Username: "username", Username: "username",
Password: "password", Password: "token",
} }
client, err := jira.NewClient(tp.Client(), "https://my.jira.com") client, err := jira.NewClient(tp.Client(), "https://my.jira.com")
@ -106,25 +111,10 @@ func main() {
} }
``` ```
#### Authenticate with session cookie #### Authenticate with session cookie [DEPRECATED]
A more thorough, [runnable example](examples/cookieauth/main.go) is provided in the examples directory. JIRA [deprecated this authentication method.](https://developer.atlassian.com/cloud/jira/platform/deprecation-notice-basic-auth-and-cookie-based-auth/) It's not longer available for use.
Note: The `AuthURL` is almost always going to have the path `/rest/auth/1/session`
```go
tp := jira.CookieAuthTransport{
Username: "username",
Password: "password",
AuthURL: "https://my.jira.com/rest/auth/1/session",
}
client, err := jira.NewClient(tp.Client(), "https://my.jira.com")
u, _, err := client.User.Get("admin")
fmt.Printf("\nEmail: %v\nSuccess!\n", u.EmailAddress)
}
```
#### Authenticate with OAuth #### Authenticate with OAuth
@ -146,10 +136,9 @@ import (
func main() { func main() {
base := "https://my.jira.com" base := "https://my.jira.com"
tp := jira.CookieAuthTransport{ tp := jira.BasicAuthTransport{
Username: "username", Username: "username",
Password: "password", Password: "token",
AuthURL: fmt.Sprintf("%s/rest/auth/1/session", base),
} }
jiraClient, err := jira.NewClient(tp.Client(), base) jiraClient, err := jira.NewClient(tp.Client(), base)
@ -200,17 +189,16 @@ import (
func main() { func main() {
base := "https://my.jira.com" base := "https://my.jira.com"
tp := jira.CookieAuthTransport{ tp := jira.BasicAuthTransport{
Username: "username", Username: "username",
Password: "password", Password: "token",
AuthURL: fmt.Sprintf("%s/rest/auth/1/session", base),
} }
jiraClient, err := jira.NewClient(tp.Client(), base) jiraClient, err := jira.NewClient(tp.Client(), base)
req, _ := jiraClient.NewRequest("GET", "rest/api/2/project", nil) req, _ := jiraClient.NewRequest("GET", "rest/api/2/project", nil)
projects := new([]jira.Project) projects := new([]jira.Project)
_, err := jiraClient.Do(req, projects) _, err = jiraClient.Do(req, projects)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -250,15 +238,15 @@ A few examples:
* Correct typos in the README / documentation * Correct typos in the README / documentation
* Reporting bugs * Reporting bugs
* Implement a new feature or endpoint * Implement a new feature or endpoint
* Sharing the love if [go-jira](https://github.com/andygrunwald/go-jira) and help people to get use to it * Sharing the love of [go-jira](https://github.com/andygrunwald/go-jira) and help people to get use to it
If you are new to pull requests, checkout [Collaborating on projects using issues and pull requests / Creating a pull request](https://help.github.com/articles/creating-a-pull-request/). If you are new to pull requests, checkout [Collaborating on projects using issues and pull requests / Creating a pull request](https://help.github.com/articles/creating-a-pull-request/).
### Dependency management ### Dependency management
`go-jira` uses `dep` for dependency management. After cloning the repo, it's easy to make sure you have the correct dependencies by running `dep ensure`. `go-jira` uses `go modules` for dependency management. After cloning the repo, it's easy to make sure you have the correct dependencies by running `go mod tidy`.
For adding new dependencies, updating dependencies, and other operations, the [Daily Dep](https://golang.github.io/dep/docs/daily-dep.html) is a good place to start. For adding new dependencies, updating dependencies, and other operations, the [Daily workflow](https://github.com/golang/go/wiki/Modules#daily-workflow) is a good place to start.
### Sandbox environment for testing ### Sandbox environment for testing

View file

@ -35,7 +35,7 @@ type Board struct {
type BoardListOptions struct { type BoardListOptions struct {
// BoardType filters results to boards of the specified type. // BoardType filters results to boards of the specified type.
// Valid values: scrum, kanban. // Valid values: scrum, kanban.
BoardType string `url:"boardType,omitempty"` BoardType string `url:"type,omitempty"`
// Name filters results to boards that match or partially match the specified name. // Name filters results to boards that match or partially match the specified name.
Name string `url:"name,omitempty"` Name string `url:"name,omitempty"`
// ProjectKeyOrID filters results to boards that are relevant to a project. // ProjectKeyOrID filters results to boards that are relevant to a project.

View file

@ -5,6 +5,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"strings"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -28,12 +29,19 @@ func NewJiraError(resp *Response, httpError error) error {
if err != nil { if err != nil {
return errors.Wrap(err, httpError.Error()) return errors.Wrap(err, httpError.Error())
} }
jerr := Error{HTTPError: httpError} jerr := Error{HTTPError: httpError}
err = json.Unmarshal(body, &jerr) contentType := resp.Header.Get("Content-Type")
if err != nil { if strings.HasPrefix(contentType, "application/json") {
httpError = errors.Wrap(errors.New("Could not parse JSON"), httpError.Error()) err = json.Unmarshal(body, &jerr)
return errors.Wrap(err, httpError.Error()) if err != nil {
httpError = errors.Wrap(errors.New("Could not parse JSON"), httpError.Error())
return errors.Wrap(err, httpError.Error())
}
} else {
if httpError == nil {
return fmt.Errorf("Got Response Status %s:%s", resp.Status, string(body))
}
return errors.Wrap(httpError, fmt.Sprintf("%s: %s", resp.Status, string(body)))
} }
return &jerr return &jerr

93
vendor/gopkg.in/andygrunwald/go-jira.v1/filter.go generated vendored Normal file
View file

@ -0,0 +1,93 @@
package jira
import "github.com/google/go-querystring/query"
import "fmt"
// FilterService handles fields for the JIRA instance / API.
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-group-Filter
type FilterService struct {
client *Client
}
// Filter represents a Filter in Jira
type Filter struct {
Self string `json:"self"`
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Owner User `json:"owner"`
Jql string `json:"jql"`
ViewURL string `json:"viewUrl"`
SearchURL string `json:"searchUrl"`
Favourite bool `json:"favourite"`
FavouritedCount int `json:"favouritedCount"`
SharePermissions []interface{} `json:"sharePermissions"`
Subscriptions struct {
Size int `json:"size"`
Items []interface{} `json:"items"`
MaxResults int `json:"max-results"`
StartIndex int `json:"start-index"`
EndIndex int `json:"end-index"`
} `json:"subscriptions"`
}
// GetList retrieves all filters from Jira
func (fs *FilterService) GetList() ([]*Filter, *Response, error) {
options := &GetQueryOptions{}
apiEndpoint := "rest/api/2/filter"
req, err := fs.client.NewRequest("GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
if options != nil {
q, err := query.Values(options)
if err != nil {
return nil, nil, err
}
req.URL.RawQuery = q.Encode()
}
filters := []*Filter{}
resp, err := fs.client.Do(req, &filters)
if err != nil {
jerr := NewJiraError(resp, err)
return nil, resp, jerr
}
return filters, resp, err
}
// GetFavouriteList retrieves the user's favourited filters from Jira
func (fs *FilterService) GetFavouriteList() ([]*Filter, *Response, error) {
apiEndpoint := "rest/api/2/filter/favourite"
req, err := fs.client.NewRequest("GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
filters := []*Filter{}
resp, err := fs.client.Do(req, &filters)
if err != nil {
jerr := NewJiraError(resp, err)
return nil, resp, jerr
}
return filters, resp, err
}
// Get retrieves a single Filter from Jira
func (fs *FilterService) Get(filterID int) (*Filter, *Response, error) {
apiEndpoint := fmt.Sprintf("rest/api/2/filter/%d", filterID)
req, err := fs.client.NewRequest("GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
filter := new(Filter)
resp, err := fs.client.Do(req, filter)
if err != nil {
jerr := NewJiraError(resp, err)
return nil, resp, jerr
}
return filter, resp, err
}

12
vendor/gopkg.in/andygrunwald/go-jira.v1/go.mod generated vendored Normal file
View file

@ -0,0 +1,12 @@
module github.com/andygrunwald/go-jira
go 1.12
require (
github.com/fatih/structs v1.0.0
github.com/google/go-cmp v0.3.0
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135
github.com/pkg/errors v0.8.0
github.com/trivago/tgo v1.0.1
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734
)

View file

@ -7,6 +7,7 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"mime/multipart" "mime/multipart"
"net/http"
"net/url" "net/url"
"reflect" "reflect"
"strings" "strings"
@ -29,6 +30,13 @@ type IssueService struct {
client *Client client *Client
} }
// UpdateQueryOptions specifies the optional parameters to the Edit issue
type UpdateQueryOptions struct {
NotifyUsers bool `url:"notifyUsers,omitempty"`
OverrideScreenSecurity bool `url:"overrideScreenSecurity,omitempty"`
OverrideEditableFlag bool `url:"overrideEditableFlag,omitempty"`
}
// Issue represents a JIRA issue. // Issue represents a JIRA issue.
type Issue struct { type Issue struct {
Expand string `json:"expand,omitempty" structs:"expand,omitempty"` Expand string `json:"expand,omitempty" structs:"expand,omitempty"`
@ -91,46 +99,47 @@ type Epic struct {
// Every JIRA issue has several fields attached. // Every JIRA issue has several fields attached.
type IssueFields struct { type IssueFields struct {
// TODO Missing fields // TODO Missing fields
// * "aggregatetimespent": null,
// * "workratio": -1, // * "workratio": -1,
// * "lastViewed": null, // * "lastViewed": null,
// * "aggregatetimeoriginalestimate": null,
// * "aggregatetimeestimate": null,
// * "environment": null, // * "environment": null,
Expand string `json:"expand,omitempty" structs:"expand,omitempty"` Expand string `json:"expand,omitempty" structs:"expand,omitempty"`
Type IssueType `json:"issuetype,omitempty" structs:"issuetype,omitempty"` Type IssueType `json:"issuetype,omitempty" structs:"issuetype,omitempty"`
Project Project `json:"project,omitempty" structs:"project,omitempty"` Project Project `json:"project,omitempty" structs:"project,omitempty"`
Resolution *Resolution `json:"resolution,omitempty" structs:"resolution,omitempty"` Resolution *Resolution `json:"resolution,omitempty" structs:"resolution,omitempty"`
Priority *Priority `json:"priority,omitempty" structs:"priority,omitempty"` Priority *Priority `json:"priority,omitempty" structs:"priority,omitempty"`
Resolutiondate Time `json:"resolutiondate,omitempty" structs:"resolutiondate,omitempty"` Resolutiondate Time `json:"resolutiondate,omitempty" structs:"resolutiondate,omitempty"`
Created Time `json:"created,omitempty" structs:"created,omitempty"` Created Time `json:"created,omitempty" structs:"created,omitempty"`
Duedate Date `json:"duedate,omitempty" structs:"duedate,omitempty"` Duedate Date `json:"duedate,omitempty" structs:"duedate,omitempty"`
Watches *Watches `json:"watches,omitempty" structs:"watches,omitempty"` Watches *Watches `json:"watches,omitempty" structs:"watches,omitempty"`
Assignee *User `json:"assignee,omitempty" structs:"assignee,omitempty"` Assignee *User `json:"assignee,omitempty" structs:"assignee,omitempty"`
Updated Time `json:"updated,omitempty" structs:"updated,omitempty"` Updated Time `json:"updated,omitempty" structs:"updated,omitempty"`
Description string `json:"description,omitempty" structs:"description,omitempty"` Description string `json:"description,omitempty" structs:"description,omitempty"`
Summary string `json:"summary,omitempty" structs:"summary,omitempty"` Summary string `json:"summary,omitempty" structs:"summary,omitempty"`
Creator *User `json:"Creator,omitempty" structs:"Creator,omitempty"` Creator *User `json:"Creator,omitempty" structs:"Creator,omitempty"`
Reporter *User `json:"reporter,omitempty" structs:"reporter,omitempty"` Reporter *User `json:"reporter,omitempty" structs:"reporter,omitempty"`
Components []*Component `json:"components,omitempty" structs:"components,omitempty"` Components []*Component `json:"components,omitempty" structs:"components,omitempty"`
Status *Status `json:"status,omitempty" structs:"status,omitempty"` Status *Status `json:"status,omitempty" structs:"status,omitempty"`
Progress *Progress `json:"progress,omitempty" structs:"progress,omitempty"` Progress *Progress `json:"progress,omitempty" structs:"progress,omitempty"`
AggregateProgress *Progress `json:"aggregateprogress,omitempty" structs:"aggregateprogress,omitempty"` AggregateProgress *Progress `json:"aggregateprogress,omitempty" structs:"aggregateprogress,omitempty"`
TimeTracking *TimeTracking `json:"timetracking,omitempty" structs:"timetracking,omitempty"` TimeTracking *TimeTracking `json:"timetracking,omitempty" structs:"timetracking,omitempty"`
TimeSpent int `json:"timespent,omitempty" structs:"timespent,omitempty"` TimeSpent int `json:"timespent,omitempty" structs:"timespent,omitempty"`
TimeEstimate int `json:"timeestimate,omitempty" structs:"timeestimate,omitempty"` TimeEstimate int `json:"timeestimate,omitempty" structs:"timeestimate,omitempty"`
TimeOriginalEstimate int `json:"timeoriginalestimate,omitempty" structs:"timeoriginalestimate,omitempty"` TimeOriginalEstimate int `json:"timeoriginalestimate,omitempty" structs:"timeoriginalestimate,omitempty"`
Worklog *Worklog `json:"worklog,omitempty" structs:"worklog,omitempty"` Worklog *Worklog `json:"worklog,omitempty" structs:"worklog,omitempty"`
IssueLinks []*IssueLink `json:"issuelinks,omitempty" structs:"issuelinks,omitempty"` IssueLinks []*IssueLink `json:"issuelinks,omitempty" structs:"issuelinks,omitempty"`
Comments *Comments `json:"comment,omitempty" structs:"comment,omitempty"` Comments *Comments `json:"comment,omitempty" structs:"comment,omitempty"`
FixVersions []*FixVersion `json:"fixVersions,omitempty" structs:"fixVersions,omitempty"` FixVersions []*FixVersion `json:"fixVersions,omitempty" structs:"fixVersions,omitempty"`
Labels []string `json:"labels,omitempty" structs:"labels,omitempty"` AffectsVersions []*AffectsVersion `json:"versions,omitempty" structs:"versions,omitempty"`
Subtasks []*Subtasks `json:"subtasks,omitempty" structs:"subtasks,omitempty"` Labels []string `json:"labels,omitempty" structs:"labels,omitempty"`
Attachments []*Attachment `json:"attachment,omitempty" structs:"attachment,omitempty"` Subtasks []*Subtasks `json:"subtasks,omitempty" structs:"subtasks,omitempty"`
Epic *Epic `json:"epic,omitempty" structs:"epic,omitempty"` Attachments []*Attachment `json:"attachment,omitempty" structs:"attachment,omitempty"`
Sprint *Sprint `json:"sprint,omitempty" structs:"sprint,omitempty"` Epic *Epic `json:"epic,omitempty" structs:"epic,omitempty"`
Parent *Parent `json:"parent,omitempty" structs:"parent,omitempty"` Sprint *Sprint `json:"sprint,omitempty" structs:"sprint,omitempty"`
Unknowns tcontainer.MarshalMap Parent *Parent `json:"parent,omitempty" structs:"parent,omitempty"`
AggregateTimeOriginalEstimate int `json:"aggregatetimeoriginalestimate,omitempty" structs:"aggregatetimeoriginalestimate,omitempty"`
AggregateTimeSpent int `json:"aggregatetimespent,omitempty" structs:"aggregatetimespent,omitempty"`
AggregateTimeEstimate int `json:"aggregatetimeestimate,omitempty" structs:"aggregatetimeestimate,omitempty"`
Unknowns tcontainer.MarshalMap
} }
// MarshalJSON is a custom JSON marshal function for the IssueFields structs. // MarshalJSON is a custom JSON marshal function for the IssueFields structs.
@ -212,6 +221,7 @@ type IssueRenderedFields struct {
Duedate string `json:"duedate,omitempty" structs:"duedate,omitempty"` Duedate string `json:"duedate,omitempty" structs:"duedate,omitempty"`
Updated string `json:"updated,omitempty" structs:"updated,omitempty"` Updated string `json:"updated,omitempty" structs:"updated,omitempty"`
Comments *Comments `json:"comment,omitempty" structs:"comment,omitempty"` Comments *Comments `json:"comment,omitempty" structs:"comment,omitempty"`
Description string `json:"description,omitempty" structs:"description,omitempty"`
} }
// IssueType represents a type of a JIRA issue. // IssueType represents a type of a JIRA issue.
@ -274,6 +284,7 @@ type Status struct {
type Progress struct { type Progress struct {
Progress int `json:"progress" structs:"progress"` Progress int `json:"progress" structs:"progress"`
Total int `json:"total" structs:"total"` Total int `json:"total" structs:"total"`
Percent int `json:"percent" structs:"percent"`
} }
// Parent represents the parent of a JIRA issue, to be used with subtask issue types. // Parent represents the parent of a JIRA issue, to be used with subtask issue types.
@ -285,6 +296,10 @@ type Parent struct {
// Time represents the Time definition of JIRA as a time.Time of go // Time represents the Time definition of JIRA as a time.Time of go
type Time time.Time type Time time.Time
func (t Time) Equal(u Time) bool {
return time.Time(t).Equal(time.Time(u))
}
// Date represents the Date definition of JIRA as a time.Time of go // Date represents the Date definition of JIRA as a time.Time of go
type Date time.Time type Date time.Time
@ -343,6 +358,12 @@ func (t *Time) UnmarshalJSON(b []byte) error {
return nil return nil
} }
// MarshalJSON will transform the time.Time into a JIRA time
// during the creation of a JIRA request
func (t Time) MarshalJSON() ([]byte, error) {
return []byte(time.Time(t).Format("\"2006-01-02T15:04:05.999-0700\"")), nil
}
// UnmarshalJSON will transform the JIRA date into a time.Time // UnmarshalJSON will transform the JIRA date into a time.Time
// during the transformation of the JIRA JSON response // during the transformation of the JIRA JSON response
func (t *Date) UnmarshalJSON(b []byte) error { func (t *Date) UnmarshalJSON(b []byte) error {
@ -378,17 +399,23 @@ type Worklog struct {
// WorklogRecord represents one entry of a Worklog // WorklogRecord represents one entry of a Worklog
type WorklogRecord struct { type WorklogRecord struct {
Self string `json:"self,omitempty" structs:"self,omitempty"` Self string `json:"self,omitempty" structs:"self,omitempty"`
Author *User `json:"author,omitempty" structs:"author,omitempty"` Author *User `json:"author,omitempty" structs:"author,omitempty"`
UpdateAuthor *User `json:"updateAuthor,omitempty" structs:"updateAuthor,omitempty"` UpdateAuthor *User `json:"updateAuthor,omitempty" structs:"updateAuthor,omitempty"`
Comment string `json:"comment,omitempty" structs:"comment,omitempty"` Comment string `json:"comment,omitempty" structs:"comment,omitempty"`
Created *Time `json:"created,omitempty" structs:"created,omitempty"` Created *Time `json:"created,omitempty" structs:"created,omitempty"`
Updated *Time `json:"updated,omitempty" structs:"updated,omitempty"` Updated *Time `json:"updated,omitempty" structs:"updated,omitempty"`
Started *Time `json:"started,omitempty" structs:"started,omitempty"` Started *Time `json:"started,omitempty" structs:"started,omitempty"`
TimeSpent string `json:"timeSpent,omitempty" structs:"timeSpent,omitempty"` TimeSpent string `json:"timeSpent,omitempty" structs:"timeSpent,omitempty"`
TimeSpentSeconds int `json:"timeSpentSeconds,omitempty" structs:"timeSpentSeconds,omitempty"` TimeSpentSeconds int `json:"timeSpentSeconds,omitempty" structs:"timeSpentSeconds,omitempty"`
ID string `json:"id,omitempty" structs:"id,omitempty"` ID string `json:"id,omitempty" structs:"id,omitempty"`
IssueID string `json:"issueId,omitempty" structs:"issueId,omitempty"` IssueID string `json:"issueId,omitempty" structs:"issueId,omitempty"`
Properties []EntityProperty `json:"properties,omitempty"`
}
type EntityProperty struct {
Key string `json:"key"`
Value interface{} `json:"value"`
} }
// TimeTracking represents the timetracking fields of a JIRA issue. // TimeTracking represents the timetracking fields of a JIRA issue.
@ -449,16 +476,21 @@ type Comment struct {
// FixVersion represents a software release in which an issue is fixed. // FixVersion represents a software release in which an issue is fixed.
type FixVersion struct { type FixVersion struct {
Archived *bool `json:"archived,omitempty" structs:"archived,omitempty"` Self string `json:"self,omitempty" structs:"self,omitempty"`
ID string `json:"id,omitempty" structs:"id,omitempty"` ID string `json:"id,omitempty" structs:"id,omitempty"`
Name string `json:"name,omitempty" structs:"name,omitempty"` Name string `json:"name,omitempty" structs:"name,omitempty"`
ProjectID int `json:"projectId,omitempty" structs:"projectId,omitempty"` Description string `json:"description,omitempty" structs:"name,omitempty"`
ReleaseDate string `json:"releaseDate,omitempty" structs:"releaseDate,omitempty"` Archived *bool `json:"archived,omitempty" structs:"archived,omitempty"`
Released *bool `json:"released,omitempty" structs:"released,omitempty"` Released *bool `json:"released,omitempty" structs:"released,omitempty"`
Self string `json:"self,omitempty" structs:"self,omitempty"` ReleaseDate string `json:"releaseDate,omitempty" structs:"releaseDate,omitempty"`
UserReleaseDate string `json:"userReleaseDate,omitempty" structs:"userReleaseDate,omitempty"` UserReleaseDate string `json:"userReleaseDate,omitempty" structs:"userReleaseDate,omitempty"`
ProjectID int `json:"projectId,omitempty" structs:"projectId,omitempty"` // Unlike other IDs, this is returned as a number
StartDate string `json:"startDate,omitempty" structs:"startDate,omitempty"`
} }
// AffectsVersion represents a software release which is affected by an issue.
type AffectsVersion Version
// CommentVisibility represents he visibility of a comment. // CommentVisibility represents he visibility of a comment.
// E.g. Type could be "role" and Value "Administrators" // E.g. Type could be "role" and Value "Administrators"
type CommentVisibility struct { type CommentVisibility struct {
@ -506,6 +538,22 @@ type GetQueryOptions struct {
ProjectKeys string `url:"projectKeys,omitempty"` ProjectKeys string `url:"projectKeys,omitempty"`
} }
// GetWorklogsQueryOptions specifies the optional parameters for the Get Worklogs method
type GetWorklogsQueryOptions struct {
StartAt int64 `url:"startAt,omitempty"`
MaxResults int32 `url:"maxResults,omitempty"`
Expand string `url:"expand,omitempty"`
}
type AddWorklogQueryOptions struct {
NotifyUsers bool `url:"notifyUsers,omitempty"`
AdjustEstimate string `url:"adjustEstimate,omitempty"`
NewEstimate string `url:"newEstimate,omitempty"`
ReduceBy string `url:"reduceBy,omitempty"`
Expand string `url:"expand,omitempty"`
OverrideEditableFlag bool `url:"overrideEditableFlag,omitempty"`
}
// CustomFields represents custom fields of JIRA // CustomFields represents custom fields of JIRA
// This can heavily differ between JIRA instances // This can heavily differ between JIRA instances
type CustomFields map[string]string type CustomFields map[string]string
@ -601,11 +649,29 @@ func (s *IssueService) PostAttachment(issueID string, r io.Reader, attachmentNam
return attachment, resp, nil return attachment, resp, nil
} }
// DeleteAttachment deletes an attachment of a given attachmentID
func (s *IssueService) DeleteAttachment(attachmentID string) (*Response, error) {
apiEndpoint := fmt.Sprintf("rest/api/2/attachment/%s", attachmentID)
req, err := s.client.NewRequest("DELETE", apiEndpoint, nil)
if err != nil {
return nil, err
}
resp, err := s.client.Do(req, nil)
if err != nil {
jerr := NewJiraError(resp, err)
return resp, jerr
}
return resp, nil
}
// GetWorklogs gets all the worklogs for an issue. // GetWorklogs gets all the worklogs for an issue.
// This method is especially important if you need to read all the worklogs, not just the first page. // This method is especially important if you need to read all the worklogs, not just the first page.
// //
// https://docs.atlassian.com/jira/REST/cloud/#api/2/issue/{issueIdOrKey}/worklog-getIssueWorklog // https://docs.atlassian.com/jira/REST/cloud/#api/2/issue/{issueIdOrKey}/worklog-getIssueWorklog
func (s *IssueService) GetWorklogs(issueID string) (*Worklog, *Response, error) { func (s *IssueService) GetWorklogs(issueID string, options ...func(*http.Request) error) (*Worklog, *Response, error) {
apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/worklog", issueID) apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/worklog", issueID)
req, err := s.client.NewRequest("GET", apiEndpoint, nil) req, err := s.client.NewRequest("GET", apiEndpoint, nil)
@ -613,18 +679,41 @@ func (s *IssueService) GetWorklogs(issueID string) (*Worklog, *Response, error)
return nil, nil, err return nil, nil, err
} }
for _, option := range options {
err = option(req)
if err != nil {
return nil, nil, err
}
}
v := new(Worklog) v := new(Worklog)
resp, err := s.client.Do(req, v) resp, err := s.client.Do(req, v)
return v, resp, err return v, resp, err
} }
// Applies query options to http request.
// This helper is meant to be used with all "QueryOptions" structs.
func WithQueryOptions(options interface{}) func(*http.Request) error {
q, err := query.Values(options)
if err != nil {
return func(*http.Request) error {
return err
}
}
return func(r *http.Request) error {
r.URL.RawQuery = q.Encode()
return 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.
// //
// JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue-createIssues // JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue-createIssues
func (s *IssueService) Create(issue *Issue) (*Issue, *Response, error) { func (s *IssueService) Create(issue *Issue) (*Issue, *Response, error) {
apiEndpoint := "rest/api/2/issue/" apiEndpoint := "rest/api/2/issue"
req, err := s.client.NewRequest("POST", apiEndpoint, issue) req, err := s.client.NewRequest("POST", apiEndpoint, issue)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -648,12 +737,17 @@ func (s *IssueService) Create(issue *Issue) (*Issue, *Response, error) {
return responseIssue, resp, nil return responseIssue, resp, nil
} }
// Update updates an issue from a JSON representation. The issue is found by key. // UpdateWithOptions updates an issue from a JSON representation,
// while also specifiying query params. The issue is found by key.
// //
// JIRA API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/issue-editIssue // JIRA API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/issue-editIssue
func (s *IssueService) Update(issue *Issue) (*Issue, *Response, error) { func (s *IssueService) UpdateWithOptions(issue *Issue, opts *UpdateQueryOptions) (*Issue, *Response, error) {
apiEndpoint := fmt.Sprintf("rest/api/2/issue/%v", issue.Key) apiEndpoint := fmt.Sprintf("rest/api/2/issue/%v", issue.Key)
req, err := s.client.NewRequest("PUT", apiEndpoint, issue) url, err := addOptions(apiEndpoint, opts)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("PUT", url, issue)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -669,6 +763,13 @@ func (s *IssueService) Update(issue *Issue) (*Issue, *Response, error) {
return &ret, resp, nil return &ret, resp, nil
} }
// Update updates an issue from a JSON representation. The issue is found by key.
//
// JIRA API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/issue-editIssue
func (s *IssueService) Update(issue *Issue) (*Issue, *Response, error) {
return s.UpdateWithOptions(issue, nil)
}
// UpdateIssue updates an issue from a JSON representation. The issue is found by key. // UpdateIssue updates an issue from a JSON representation. The issue is found by key.
// //
// https://docs.atlassian.com/jira/REST/7.4.0/#api/2/issue-editIssue // https://docs.atlassian.com/jira/REST/7.4.0/#api/2/issue-editIssue
@ -718,7 +819,7 @@ func (s *IssueService) UpdateComment(issueID string, comment *Comment) (*Comment
Body: comment.Body, Body: comment.Body,
} }
apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/comment/%s", issueID, comment.ID) apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/comment/%s", issueID, comment.ID)
req, err := s.client.NewRequest("POST", apiEndpoint, reqBody) req, err := s.client.NewRequest("PUT", apiEndpoint, reqBody)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -732,16 +833,42 @@ func (s *IssueService) UpdateComment(issueID string, comment *Comment) (*Comment
return responseComment, resp, nil return responseComment, resp, nil
} }
// DeleteComment Deletes a comment from an issueID.
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-issue-issueIdOrKey-comment-id-delete
func (s *IssueService) DeleteComment(issueID, commentID string) error {
apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/comment/%s", issueID, commentID)
req, err := s.client.NewRequest("DELETE", apiEndpoint, nil)
if err != nil {
return err
}
resp, err := s.client.Do(req, nil)
if err != nil {
jerr := NewJiraError(resp, err)
return jerr
}
return nil
}
// AddWorklogRecord adds a new worklog record to issueID. // AddWorklogRecord adds a new worklog record to issueID.
// //
// https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-issue-issueIdOrKey-worklog-post // https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-issue-issueIdOrKey-worklog-post
func (s *IssueService) AddWorklogRecord(issueID string, record *WorklogRecord) (*WorklogRecord, *Response, error) { func (s *IssueService) AddWorklogRecord(issueID string, record *WorklogRecord, options ...func(*http.Request) error) (*WorklogRecord, *Response, error) {
apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/worklog", issueID) apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/worklog", issueID)
req, err := s.client.NewRequest("POST", apiEndpoint, record) req, err := s.client.NewRequest("POST", apiEndpoint, record)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
for _, option := range options {
err = option(req)
if err != nil {
return nil, nil, err
}
}
responseRecord := new(WorklogRecord) responseRecord := new(WorklogRecord)
resp, err := s.client.Do(req, responseRecord) resp, err := s.client.Do(req, responseRecord)
if err != nil { if err != nil {
@ -778,8 +905,22 @@ func (s *IssueService) Search(jql string, options *SearchOptions) ([]Issue, *Res
if options == nil { if options == nil {
u = fmt.Sprintf("rest/api/2/search?jql=%s", url.QueryEscape(jql)) u = fmt.Sprintf("rest/api/2/search?jql=%s", url.QueryEscape(jql))
} else { } else {
u = fmt.Sprintf("rest/api/2/search?jql=%s&startAt=%d&maxResults=%d&expand=%s&fields=%s&validateQuery=%s", url.QueryEscape(jql), u = "rest/api/2/search?jql=" + url.QueryEscape(jql)
options.StartAt, options.MaxResults, options.Expand, strings.Join(options.Fields, ","), options.ValidateQuery) if options.StartAt != 0 {
u += fmt.Sprintf("&startAt=%d", options.StartAt)
}
if options.MaxResults != 0 {
u += fmt.Sprintf("&maxResults=%d", options.MaxResults)
}
if options.Expand != "" {
u += fmt.Sprintf("&expand=%s", options.Expand)
}
if strings.Join(options.Fields, ",") != "" {
u += fmt.Sprintf("&fields=%s", strings.Join(options.Fields, ","))
}
if options.ValidateQuery != "" {
u += fmt.Sprintf("&validateQuery=%s", options.ValidateQuery)
}
} }
req, err := s.client.NewRequest("GET", u, nil) req, err := s.client.NewRequest("GET", u, nil)
@ -1088,3 +1229,32 @@ func (s *IssueService) RemoveWatcher(issueID string, userName string) (*Response
return resp, err return resp, err
} }
// UpdateAssignee updates the user assigned to work on the given issue
//
// JIRA API docs: https://docs.atlassian.com/software/jira/docs/api/REST/7.10.2/#api/2/issue-assign
func (s *IssueService) UpdateAssignee(issueID string, assignee *User) (*Response, error) {
apiEndPoint := fmt.Sprintf("rest/api/2/issue/%s/assignee", issueID)
req, err := s.client.NewRequest("PUT", apiEndPoint, assignee)
if err != nil {
return nil, err
}
resp, err := s.client.Do(req, nil)
if err != nil {
err = NewJiraError(resp, err)
}
return resp, err
}
func (c ChangelogHistory) CreatedTime() (time.Time, error) {
var t time.Time
// Ignore null
if string(c.Created) == "null" {
return t, nil
}
t, err := time.Parse("2006-01-02T15:04:05.999-0700", c.Created)
return t, err
}

View file

@ -27,19 +27,22 @@ type Client struct {
session *Session session *Session
// Services used for talking to different parts of the JIRA API. // Services used for talking to different parts of the JIRA API.
Authentication *AuthenticationService Authentication *AuthenticationService
Issue *IssueService Issue *IssueService
Project *ProjectService Project *ProjectService
Board *BoardService Board *BoardService
Sprint *SprintService Sprint *SprintService
User *UserService User *UserService
Group *GroupService Group *GroupService
Version *VersionService Version *VersionService
Priority *PriorityService Priority *PriorityService
Field *FieldService Field *FieldService
Component *ComponentService Component *ComponentService
Resolution *ResolutionService Resolution *ResolutionService
StatusCategory *StatusCategoryService StatusCategory *StatusCategoryService
Filter *FilterService
Role *RoleService
PermissionScheme *PermissionSchemeService
} }
// NewClient returns a new JIRA API client. // NewClient returns a new JIRA API client.
@ -81,6 +84,9 @@ func NewClient(httpClient *http.Client, baseURL string) (*Client, error) {
c.Component = &ComponentService{client: c} c.Component = &ComponentService{client: c}
c.Resolution = &ResolutionService{client: c} c.Resolution = &ResolutionService{client: c}
c.StatusCategory = &StatusCategoryService{client: c} c.StatusCategory = &StatusCategoryService{client: c}
c.Filter = &FilterService{client: c}
c.Role = &RoleService{client: c}
c.PermissionScheme = &PermissionSchemeService{client: c}
return c, nil return c, nil
} }
@ -375,7 +381,10 @@ func (t *CookieAuthTransport) RoundTrip(req *http.Request) (*http.Response, erro
req2 := cloneRequest(req) // per RoundTripper contract req2 := cloneRequest(req) // per RoundTripper contract
for _, cookie := range t.SessionObject { for _, cookie := range t.SessionObject {
req2.AddCookie(cookie) // Don't add an empty value cookie to the request
if cookie.Value != "" {
req2.AddCookie(cookie)
}
} }
return t.transport().RoundTrip(req2) return t.transport().RoundTrip(req2)

View file

@ -0,0 +1,69 @@
package jira
import "fmt"
// PermissionSchemeService handles permissionschemes for the JIRA instance / API.
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-group-Permissionscheme
type PermissionSchemeService struct {
client *Client
}
type PermissionSchemes struct {
PermissionSchemes []PermissionScheme `json:"permissionSchemes" structs:"permissionSchemes"`
}
type Permission struct {
ID int `json:"id" structs:"id"`
Self string `json:"expand" structs:"expand"`
Holder Holder `json:"holder" structs:"holder"`
Name string `json:"permission" structs:"permission"`
}
type Holder struct {
Type string `json:"type" structs:"type"`
Parameter string `json:"parameter" structs:"parameter"`
Expand string `json:"expand" structs:"expand"`
}
// GetList returns a list of all permission schemes
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-permissionscheme-get
func (s *PermissionSchemeService) GetList() (*PermissionSchemes, *Response, error) {
apiEndpoint := "/rest/api/3/permissionscheme"
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
pss := new(PermissionSchemes)
resp, err := s.client.Do(req, &pss)
if err != nil {
jerr := NewJiraError(resp, err)
return nil, resp, jerr
}
return pss, resp, nil
}
// Get returns a full representation of the permission scheme for the schemeID
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-permissionscheme-schemeId-get
func (s *PermissionSchemeService) Get(schemeID int) (*PermissionScheme, *Response, error) {
apiEndpoint := fmt.Sprintf("/rest/api/3/permissionscheme/%d", schemeID)
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
ps := new(PermissionScheme)
resp, err := s.client.Do(req, ps)
if err != nil {
jerr := NewJiraError(resp, err)
return nil, resp, jerr
}
if ps.Self == "" {
return nil, resp, fmt.Errorf("No permissionscheme with ID %d found", schemeID)
}
return ps, resp, nil
}

View file

@ -36,24 +36,22 @@ type ProjectCategory struct {
// Project represents a JIRA Project. // Project represents a JIRA Project.
type Project struct { type Project struct {
Expand string `json:"expand,omitempty" structs:"expand,omitempty"` Expand string `json:"expand,omitempty" structs:"expand,omitempty"`
Self string `json:"self,omitempty" structs:"self,omitempty"` Self string `json:"self,omitempty" structs:"self,omitempty"`
ID string `json:"id,omitempty" structs:"id,omitempty"` ID string `json:"id,omitempty" structs:"id,omitempty"`
Key string `json:"key,omitempty" structs:"key,omitempty"` Key string `json:"key,omitempty" structs:"key,omitempty"`
Description string `json:"description,omitempty" structs:"description,omitempty"` Description string `json:"description,omitempty" structs:"description,omitempty"`
Lead User `json:"lead,omitempty" structs:"lead,omitempty"` Lead User `json:"lead,omitempty" structs:"lead,omitempty"`
Components []ProjectComponent `json:"components,omitempty" structs:"components,omitempty"` Components []ProjectComponent `json:"components,omitempty" structs:"components,omitempty"`
IssueTypes []IssueType `json:"issueTypes,omitempty" structs:"issueTypes,omitempty"` IssueTypes []IssueType `json:"issueTypes,omitempty" structs:"issueTypes,omitempty"`
URL string `json:"url,omitempty" structs:"url,omitempty"` URL string `json:"url,omitempty" structs:"url,omitempty"`
Email string `json:"email,omitempty" structs:"email,omitempty"` Email string `json:"email,omitempty" structs:"email,omitempty"`
AssigneeType string `json:"assigneeType,omitempty" structs:"assigneeType,omitempty"` AssigneeType string `json:"assigneeType,omitempty" structs:"assigneeType,omitempty"`
Versions []Version `json:"versions,omitempty" structs:"versions,omitempty"` Versions []Version `json:"versions,omitempty" structs:"versions,omitempty"`
Name string `json:"name,omitempty" structs:"name,omitempty"` Name string `json:"name,omitempty" structs:"name,omitempty"`
Roles struct { Roles map[string]string `json:"roles,omitempty" structs:"roles,omitempty"`
Developers string `json:"Developers,omitempty" structs:"Developers,omitempty"` AvatarUrls AvatarUrls `json:"avatarUrls,omitempty" structs:"avatarUrls,omitempty"`
} `json:"roles,omitempty" structs:"roles,omitempty"` ProjectCategory ProjectCategory `json:"projectCategory,omitempty" structs:"projectCategory,omitempty"`
AvatarUrls AvatarUrls `json:"avatarUrls,omitempty" structs:"avatarUrls,omitempty"`
ProjectCategory ProjectCategory `json:"projectCategory,omitempty" structs:"projectCategory,omitempty"`
} }
// ProjectComponent represents a single component of a project // ProjectComponent represents a single component of a project
@ -74,11 +72,12 @@ type ProjectComponent struct {
// PermissionScheme represents the permission scheme for the project // PermissionScheme represents the permission scheme for the project
type PermissionScheme struct { type PermissionScheme struct {
Expand string `json:"expand" structs:"expand,omitempty"` Expand string `json:"expand" structs:"expand,omitempty"`
Self string `json:"self" structs:"self,omitempty"` Self string `json:"self" structs:"self,omitempty"`
ID int `json:"id" structs:"id,omitempty"` ID int `json:"id" structs:"id,omitempty"`
Name string `json:"name" structs:"name,omitempty"` Name string `json:"name" structs:"name,omitempty"`
Description string `json:"description" structs:"description,omitempty"` Description string `json:"description" structs:"description,omitempty"`
Permissions []Permission `json:"permissions" structs:"permissions,omitempty"`
} }
// GetList gets all projects form JIRA // GetList gets all projects form JIRA

76
vendor/gopkg.in/andygrunwald/go-jira.v1/role.go generated vendored Normal file
View file

@ -0,0 +1,76 @@
package jira
import (
"fmt"
)
// RoleService handles roles for the JIRA instance / API.
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-group-Role
type RoleService struct {
client *Client
}
// Role represents a JIRA product role
type Role struct {
Self string `json:"self" structs:"self"`
Name string `json:"name" structs:"name"`
ID int `json:"id" structs:"id"`
Description string `json:"description" structs:"description"`
Actors []*Actor `json:"actors" structs:"actors"`
}
// Actor represents a JIRA actor
type Actor struct {
ID int `json:"id" structs:"id"`
DisplayName string `json:"displayName" structs:"displayName"`
Type string `json:"type" structs:"type"`
Name string `json:"name" structs:"name"`
AvatarURL string `json:"avatarUrl" structs:"avatarUrl"`
ActorUser *ActorUser `json:"actorUser" structs:"actoruser"`
}
// ActorUser contains the account id of the actor/user
type ActorUser struct {
AccountID string `json:"accountId" structs:"accountId"`
}
// GetList returns a list of all available project roles
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-role-get
func (s *RoleService) GetList() (*[]Role, *Response, error) {
apiEndpoint := "rest/api/3/role"
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
roles := new([]Role)
resp, err := s.client.Do(req, roles)
if err != nil {
jerr := NewJiraError(resp, err)
return nil, resp, jerr
}
return roles, resp, err
}
// Get retreives a single Role from Jira
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-role-id-get
func (s *RoleService) Get(roleID int) (*Role, *Response, error) {
apiEndpoint := fmt.Sprintf("rest/api/3/role/%d", roleID)
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
role := new(Role)
resp, err := s.client.Do(req, role)
if err != nil {
jerr := NewJiraError(resp, err)
return nil, resp, jerr
}
if role.Self == "" {
return nil, resp, fmt.Errorf("No role with ID %d found", roleID)
}
return role, resp, err
}

View file

@ -15,10 +15,13 @@ type UserService struct {
// User represents a JIRA user. // User represents a JIRA user.
type User struct { type User struct {
Self string `json:"self,omitempty" structs:"self,omitempty"` Self string `json:"self,omitempty" structs:"self,omitempty"`
AccountID string `json:"accountId,omitempty" structs:"accountId,omitempty"`
// TODO: name & key are deprecated, see:
// https://developer.atlassian.com/cloud/jira/platform/api-changes-for-user-privacy-announcement/
Name string `json:"name,omitempty" structs:"name,omitempty"` Name string `json:"name,omitempty" structs:"name,omitempty"`
Password string `json:"-"`
Key string `json:"key,omitempty" structs:"key,omitempty"` Key string `json:"key,omitempty" structs:"key,omitempty"`
Password string `json:"-"`
EmailAddress string `json:"emailAddress,omitempty" structs:"emailAddress,omitempty"` EmailAddress string `json:"emailAddress,omitempty" structs:"emailAddress,omitempty"`
AvatarUrls AvatarUrls `json:"avatarUrls,omitempty" structs:"avatarUrls,omitempty"` AvatarUrls AvatarUrls `json:"avatarUrls,omitempty" structs:"avatarUrls,omitempty"`
DisplayName string `json:"displayName,omitempty" structs:"displayName,omitempty"` DisplayName string `json:"displayName,omitempty" structs:"displayName,omitempty"`
@ -33,6 +36,15 @@ type UserGroup struct {
Name string `json:"name,omitempty" structs:"name,omitempty"` Name string `json:"name,omitempty" structs:"name,omitempty"`
} }
type userSearchParam struct {
name string
value string
}
type userSearch []userSearchParam
type userSearchF func(userSearch) userSearch
// Get gets user info from JIRA // Get gets user info from JIRA
// //
// JIRA API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/user-getUser // JIRA API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/user-getUser
@ -81,6 +93,24 @@ func (s *UserService) Create(user *User) (*User, *Response, error) {
return responseUser, resp, nil return responseUser, resp, nil
} }
// Delete deletes an user from JIRA.
// Returns http.StatusNoContent on success.
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-user-delete
func (s *UserService) Delete(username string) (*Response, error) {
apiEndpoint := fmt.Sprintf("/rest/api/2/user?username=%s", username)
req, err := s.client.NewRequest("DELETE", apiEndpoint, nil)
if err != nil {
return nil, err
}
resp, err := s.client.Do(req, nil)
if err != nil {
return resp, NewJiraError(resp, err)
}
return resp, nil
}
// GetGroups returns the groups which the user belongs to // GetGroups returns the groups which the user belongs to
// //
// JIRA API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/user-getUserGroups // JIRA API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/user-getUserGroups
@ -116,12 +146,59 @@ func (s *UserService) GetSelf() (*User, *Response, error) {
return &user, resp, nil return &user, resp, nil
} }
// WithMaxResults sets the max results to return
func WithMaxResults(maxResults int) userSearchF {
return func(s userSearch) userSearch {
s = append(s, userSearchParam{name: "maxResults", value: fmt.Sprintf("%d", maxResults)})
return s
}
}
// WithStartAt set the start pager
func WithStartAt(startAt int) userSearchF {
return func(s userSearch) userSearch {
s = append(s, userSearchParam{name: "startAt", value: fmt.Sprintf("%d", startAt)})
return s
}
}
// WithActive sets the active users lookup
func WithActive(active bool) userSearchF {
return func(s userSearch) userSearch {
s = append(s, userSearchParam{name: "includeActive", value: fmt.Sprintf("%t", active)})
return s
}
}
// WithInactive sets the inactive users lookup
func WithInactive(inactive bool) userSearchF {
return func(s userSearch) userSearch {
s = append(s, userSearchParam{name: "includeInactive", value: fmt.Sprintf("%t", inactive)})
return s
}
}
// Find searches for user info from JIRA: // Find searches for user info from JIRA:
// It can find users by email, username or name // It can find users by email, username or name
// //
// JIRA API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/user-findUsers // JIRA API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/user-findUsers
func (s *UserService) Find(property string) ([]User, *Response, error) { func (s *UserService) Find(property string, tweaks ...userSearchF) ([]User, *Response, error) {
apiEndpoint := fmt.Sprintf("/rest/api/2/user/search?username=%s", property) search := []userSearchParam{
{
name: "username",
value: property,
},
}
for _, f := range tweaks {
search = f(search)
}
var queryString = ""
for _, param := range search {
queryString += param.name + "=" + param.value + "&"
}
apiEndpoint := fmt.Sprintf("/rest/api/2/user/search?%s", queryString[:len(queryString)-1])
req, err := s.client.NewRequest("GET", apiEndpoint, nil) req, err := s.client.NewRequest("GET", apiEndpoint, nil)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err

View file

@ -24,6 +24,7 @@ type Version struct {
ReleaseDate string `json:"releaseDate,omitempty" structs:"releaseDate,omitempty"` ReleaseDate string `json:"releaseDate,omitempty" structs:"releaseDate,omitempty"`
UserReleaseDate string `json:"userReleaseDate,omitempty" structs:"userReleaseDate,omitempty"` UserReleaseDate string `json:"userReleaseDate,omitempty" structs:"userReleaseDate,omitempty"`
ProjectID int `json:"projectId,omitempty" structs:"projectId,omitempty"` // Unlike other IDs, this is returned as a number ProjectID int `json:"projectId,omitempty" structs:"projectId,omitempty"` // Unlike other IDs, this is returned as a number
StartDate string `json:"startDate,omitempty" structs:"startDate,omitempty"`
} }
// Get gets version info from JIRA // Get gets version info from JIRA