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

Bump version to 5.11.0

This commit is contained in:
Harvey Kandola 2024-01-10 14:47:40 -05:00
parent a32510b8e6
commit 510e1bd0bd
370 changed files with 18825 additions and 5454 deletions

View file

@ -27,3 +27,4 @@ _testmain.go
*.prof
*.iml
.idea
.DS_Store

View file

@ -1,20 +0,0 @@
language: go
sudo: false
go:
- "1.9.x"
- "1.10.x"
- "1.11.x"
- "1.12.x"
- "1.13.x"
before_install:
- go get -t ./...
matrix:
allow_failures:
- go: 1.13.x
script:
- GOMAXPROCS=4 GORACE="halt_on_error=1" go test -race -v ./...

View file

@ -2,6 +2,32 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
## [1.13.0](https://github.com/andygrunwald/go-jira/compare/v1.11.1...v1.13.0) (2020-10-25)
### Features
* add AddRemoteLink method ([f200e15](https://github.com/andygrunwald/go-jira/commit/f200e158b997a303db081cbbc5a9d8ad5d89566d)), closes [/developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2](https://github.com/andygrunwald//developer.atlassian.com/cloud/jira/platform/rest/v2//issues/api-rest-api-2)
* Add Names support on Issue struct ([#278](https://github.com/andygrunwald/go-jira/issues/278)) ([1fc10e0](https://github.com/andygrunwald/go-jira/commit/1fc10e0606784f745673ccc4d8d706c36f385a7a))
* Extend Makefile for more source code quality targets ([5e52236](https://github.com/andygrunwald/go-jira/commit/5e5223631a29d10a13e598318a6abe47384e2982))
* **context:** Add support for context package ([e1f4265](https://github.com/andygrunwald/go-jira/commit/e1f4265e2b467b938fe0c095caf6d36f3136d2ff))
* **issues:** Add GetEditMeta on issue ([a783764](https://github.com/andygrunwald/go-jira/commit/a783764b52dc890773658ddd0483a9d0393e385d)), closes [/docs.atlassian.com/DAC/rest/jira/6.1.html#d2e1364](https://github.com/andygrunwald//docs.atlassian.com/DAC/rest/jira/6.1.html/issues/d2e1364)
* **IssueService:** allow empty JQL ([#268](https://github.com/andygrunwald/go-jira/issues/268)) ([4b91cf2](https://github.com/andygrunwald/go-jira/commit/4b91cf2b135355de7ecee41727c3e65f4e7067bc))
* **project:** Add cronjob to check for stale issues ([#287](https://github.com/andygrunwald/go-jira/issues/287)) ([2096b04](https://github.com/andygrunwald/go-jira/commit/2096b04e52b434c1fb1c841bab487a94674a271e))
* **project:** Add GitHub Actions testing workflow ([#289](https://github.com/andygrunwald/go-jira/issues/289)) ([80c0282](https://github.com/andygrunwald/go-jira/commit/80c02828ca9e4eb0e4a1877275baae14d330a2d9)), closes [#290](https://github.com/andygrunwald/go-jira/issues/290)
* **project:** Add workflow to greet new contributors ([#288](https://github.com/andygrunwald/go-jira/issues/288)) ([c357b61](https://github.com/andygrunwald/go-jira/commit/c357b61a40f62a919ebd94a555390958f99c8db7))
### Bug Fixes
* change millisecond time format ([8c77107](https://github.com/andygrunwald/go-jira/commit/8c77107df3757c4ec5eae6e9d7c018618e708bfa))
* paging with load balancer going to endless loop ([19d3fc0](https://github.com/andygrunwald/go-jira/commit/19d3fc0aecde547ffe1ab547c5ffb6c7972d387c)), closes [#260](https://github.com/andygrunwald/go-jira/issues/260)
* **issue:** IssueService.Search() with a not empty JQL triggers 400 bad request ([#292](https://github.com/andygrunwald/go-jira/issues/292)) ([8b64c7f](https://github.com/andygrunwald/go-jira/commit/8b64c7f005fbceb11fa43a7aff3de61eb3166fca)), closes [#291](https://github.com/andygrunwald/go-jira/issues/291)
* **IssueService.GetWatchers:** UserService.GetByAccountID support accountId params ([436469b](https://github.com/andygrunwald/go-jira/commit/436469b62d4d62037f380b38c918a13f4a5f0ab2))
* **product:** Make product naming consistent, rename JIRA to Jira ([#286](https://github.com/andygrunwald/go-jira/issues/286)) ([146229d](https://github.com/andygrunwald/go-jira/commit/146229d2ab58a3fb128ddc8dcbe03aff72e20857)), closes [#284](https://github.com/andygrunwald/go-jira/issues/284)
* **tests:** Fix TestIssueService_PostAttachment unit test ([f6b1dca](https://github.com/andygrunwald/go-jira/commit/f6b1dcafcfdd8fe69f842b1053c4030da6c97c7f))
* removing the use of username field in searching for users ([#297](https://github.com/andygrunwald/go-jira/issues/297)) ([f50cb07](https://github.com/andygrunwald/go-jira/commit/f50cb07b297d79138b13e5ab49ea33965d32f5c1))
## [1.12.0](https://github.com/andygrunwald/go-jira/compare/v1.11.1...v1.12.0) (2019-12-14)
@ -76,6 +102,3 @@ All notable changes to this project will be documented in this file. See [standa
* 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,2 +1,25 @@
test:
go test -v ./...
.DEFAULT_GOAL := help
.PHONY: help
help: ## Outputs the help.
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
.PHONY: test
test: ## Runs all unit, integration and example tests.
go test -race -v ./...
.PHONY: vet
vet: ## Runs go vet (to detect suspicious constructs).
go vet ./...
.PHONY: fmt
fmt: ## Runs go fmt (to check for go coding guidelines).
gofmt -d -s .
.PHONY: staticcheck
staticcheck: ## Runs static analysis to prevend bugs, foster code simplicity, performance and editor integration.
go install honnef.co/go/tools/cmd/staticcheck@2022.1
staticcheck ./...
.PHONY: all
all: test vet fmt staticcheck ## Runs all source code quality targets (like test, vet, fmt, staticcheck)

View file

@ -1,15 +0,0 @@
# PR Description
_What does this fix or add?_
# Checklist
* [ ] Tests added
* [ ] Good Path
* [ ] Error Path
* [ ] Commits follow conventions described here:
* [ ] [https://conventionalcommits.org/en/v1.0.0-beta.4/#summary](https://conventionalcommits.org/en/v1.0.0-beta.4/#summary)
* [ ] [https://chris.beams.io/posts/git-commit/#seven-rules](https://chris.beams.io/posts/git-commit/#seven-rules)
* [ ] Commits are squashed such that
* [ ] There is 1 commit per isolated change
* [ ] I've not made extraneous commits/changes that are unrelated to my change.

View file

@ -1,26 +1,29 @@
# go-jira
[![GoDoc](https://godoc.org/github.com/andygrunwald/go-jira?status.svg)](https://godoc.org/github.com/andygrunwald/go-jira)
[![Build Status](https://travis-ci.org/andygrunwald/go-jira.svg?branch=master)](https://travis-ci.org/andygrunwald/go-jira)
[![Build Status](https://github.com/andygrunwald/go-jira/actions/workflows/testing.yml/badge.svg)](https://github.com/andygrunwald/go-jira/actions/workflows/testing.yml)
[![Go Report Card](https://goreportcard.com/badge/github.com/andygrunwald/go-jira)](https://goreportcard.com/report/github.com/andygrunwald/go-jira)
[Go](https://golang.org/) client library for [Atlassian JIRA](https://www.atlassian.com/software/jira).
[Go](https://golang.org/) client library for [Atlassian Jira](https://www.atlassian.com/software/jira).
![Go client library for Atlassian JIRA](./img/logo_small.png "Go client library for Atlassian JIRA.")
![Go client library for Atlassian Jira](./img/logo_small.png "Go client library for Atlassian Jira.")
## Features
* Authentication (HTTP Basic, OAuth, Session Cookie)
* Create and retrieve issues
* Create and retrieve issue transitions (status updates)
* Call every API endpoint of the JIRA, even if it is not directly implemented in this library
* Call every API endpoint of the Jira, even if it is not directly implemented in this library
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/).
## Requirements
* Go >= 1.8
* JIRA v6.3.4 & v7.1.2.
* Go >= 1.14
* Jira v6.3.4 & v7.1.2.
Note that we also run our tests against 1.13, though only the last two versions
of Go are officially supported.
## Installation
@ -52,7 +55,7 @@ go test -v ./...
Please have a look at the [GoDoc documentation](https://godoc.org/github.com/andygrunwald/go-jira) for a detailed API description.
The [latest JIRA REST API documentation](https://docs.atlassian.com/jira/REST/latest/) was the base document for this package.
The [latest Jira REST API documentation](https://docs.atlassian.com/jira/REST/latest/) was the base document for this package.
## Examples
@ -68,7 +71,7 @@ package main
import (
"fmt"
"github.com/andygrunwald/go-jira"
jira "github.com/andygrunwald/go-jira"
)
func main() {
@ -92,9 +95,11 @@ an `http.Client`. That client can then be passed into the `NewClient` function
For convenience, capability for basic and cookie-based authentication is included in the main library.
#### Basic auth example
#### Token (Jira on Atlassian Cloud)
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)
Token-based authentication uses the basic authentication scheme, with a user-generated API token in place of a user's password. You can generate a token for your user [here](https://id.atlassian.com/manage-profile/security/api-tokens). Additional information about Atlassian Cloud API tokens can be found [here](https://confluence.atlassian.com/cloud/api-tokens-938839638.html).
A more thorough, [runnable example](examples/basicauth/main.go) is provided in the examples directory.
```go
func main() {
@ -111,14 +116,15 @@ func main() {
}
```
#### Authenticate with session cookie [DEPRECATED]
#### Basic (self-hosted Jira)
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.
Password-based API authentication works for self-hosted Jira **only**, and has been [deprecated for users of Atlassian Cloud](https://developer.atlassian.com/cloud/jira/platform/deprecation-notice-basic-auth-and-cookie-based-auth/).
The above token authentication example may be used, substituting a user's password for a generated token.
#### Authenticate with OAuth
If you want to connect via OAuth to your JIRA Cloud instance checkout the [example of using OAuth authentication with JIRA in Go](https://gist.github.com/Lupus/edafe9a7c5c6b13407293d795442fe67) by [@Lupus](https://github.com/Lupus).
If you want to connect via OAuth to your Jira Cloud instance checkout the [example of using OAuth authentication with Jira in Go](https://gist.github.com/Lupus/edafe9a7c5c6b13407293d795442fe67) by [@Lupus](https://github.com/Lupus).
For more details have a look at the [issue #56](https://github.com/andygrunwald/go-jira/issues/56).
@ -173,11 +179,62 @@ func main() {
}
```
### Change an issue status
This is how one can change an issue status. In this example, we change the issue from "To Do" to "In Progress."
```go
package main
import (
"fmt"
"github.com/andygrunwald/go-jira"
)
func main() {
base := "https://my.jira.com"
tp := jira.BasicAuthTransport{
Username: "username",
Password: "token",
}
jiraClient, err := jira.NewClient(tp.Client(), base)
if err != nil {
panic(err)
}
issue, _, _ := jiraClient.Issue.Get("FART-1", nil)
currentStatus := issue.Fields.Status.Name
fmt.Printf("Current status: %s\n", currentStatus)
var transitionID string
possibleTransitions, _, _ := jiraClient.Issue.GetTransitions("FART-1")
for _, v := range possibleTransitions {
if v.Name == "In Progress" {
transitionID = v.ID
break
}
}
jiraClient.Issue.DoTransition("FART-1", transitionID)
issue, _, _ = jiraClient.Issue.Get(testIssueID, nil)
fmt.Printf("Status after transition: %+v\n", issue.Fields.Status.Name)
}
```
### Get all the issues for JQL with Pagination
Jira API has limit on maxResults it can return. You may have a usecase where you need to get all issues for given JQL.
This example shows reference implementation of GetAllIssues function which does pagination on Jira API to get all the issues for given JQL
please look at [Pagination Example](https://github.com/andygrunwald/go-jira/blob/master/examples/pagination/main.go)
### Call a not implemented API endpoint
Not all API endpoints of the JIRA API are implemented into *go-jira*.
Not all API endpoints of the Jira API are implemented into *go-jira*.
But you can call them anyway:
Lets get all public projects of [Atlassian`s JIRA instance](https://jira.atlassian.com/).
Lets get all public projects of [Atlassian`s Jira instance](https://jira.atlassian.com/).
```go
package main
@ -209,7 +266,7 @@ func main() {
// ...
// BAM: Bamboo
// BAMJ: Bamboo JIRA Plugin
// BAMJ: Bamboo Jira Plugin
// CLOV: Clover
// CONF: Confluence
// ...
@ -218,7 +275,7 @@ func main() {
## Implementations
* [andygrunwald/jitic](https://github.com/andygrunwald/jitic) - The JIRA Ticket Checker
* [andygrunwald/jitic](https://github.com/andygrunwald/jitic) - The Jira Ticket Checker
## Code structure
@ -226,7 +283,7 @@ The code structure of this package was inspired by [google/go-github](https://gi
There is one main part (the client).
Based on this main client the other endpoints, like Issues or Authentication are extracted in services. E.g. `IssueService` or `AuthenticationService`.
These services own a responsibility of the single endpoints / usecases of JIRA.
These services own a responsibility of the single endpoints / usecases of Jira.
## Contribution
@ -258,7 +315,7 @@ You can read more about them at https://developer.atlassian.com/blog/2016/04/clo
## Releasing
Install `standard-version`
Install [standard-version](https://github.com/conventional-changelog/standard-version)
```bash
npm i -g standard-version
```

View file

@ -1,6 +1,7 @@
package jira
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
@ -14,9 +15,9 @@ const (
authTypeSession = 2
)
// AuthenticationService handles authentication for the JIRA instance / API.
// AuthenticationService handles authentication for the Jira instance / API.
//
// JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#authentication
// Jira API docs: https://docs.atlassian.com/jira/REST/latest/#authentication
type AuthenticationService struct {
client *Client
@ -30,7 +31,7 @@ type AuthenticationService struct {
password string
}
// Session represents a Session JSON response by the JIRA API.
// Session represents a Session JSON response by the Jira API.
type Session struct {
Self string `json:"self,omitempty"`
Name string `json:"name,omitempty"`
@ -47,16 +48,16 @@ type Session struct {
Cookies []*http.Cookie
}
// AcquireSessionCookie creates a new session for a user in JIRA.
// Once a session has been successfully created it can be used to access any of JIRA's remote APIs and also the web UI by passing the appropriate HTTP Cookie header.
// AcquireSessionCookieWithContext creates a new session for a user in Jira.
// Once a session has been successfully created it can be used to access any of Jira's remote APIs and also the web UI by passing the appropriate HTTP Cookie header.
// The header will by automatically applied to every API request.
// Note that it is generally preferrable to use HTTP BASIC authentication with the REST API.
// However, this resource may be used to mimic the behaviour of JIRA's log-in page (e.g. to display log-in errors to a user).
// However, this resource may be used to mimic the behaviour of Jira's log-in page (e.g. to display log-in errors to a user).
//
// JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#auth/1/session
// Jira API docs: https://docs.atlassian.com/jira/REST/latest/#auth/1/session
//
// Deprecated: Use CookieAuthTransport instead
func (s *AuthenticationService) AcquireSessionCookie(username, password string) (bool, error) {
func (s *AuthenticationService) AcquireSessionCookieWithContext(ctx context.Context, username, password string) (bool, error) {
apiEndpoint := "rest/auth/1/session"
body := struct {
Username string `json:"username"`
@ -66,7 +67,7 @@ func (s *AuthenticationService) AcquireSessionCookie(username, password string)
password,
}
req, err := s.client.NewRequest("POST", apiEndpoint, body)
req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, body)
if err != nil {
return false, err
}
@ -79,10 +80,10 @@ func (s *AuthenticationService) AcquireSessionCookie(username, password string)
}
if err != nil {
return false, fmt.Errorf("Auth at JIRA instance failed (HTTP(S) request). %s", err)
return false, fmt.Errorf("auth at Jira instance failed (HTTP(S) request). %s", err)
}
if resp != nil && resp.StatusCode != 200 {
return false, fmt.Errorf("Auth at JIRA instance failed (HTTP(S) request). Status code: %d", resp.StatusCode)
return false, fmt.Errorf("auth at Jira instance failed (HTTP(S) request). Status code: %d", resp.StatusCode)
}
s.client.session = session
@ -91,7 +92,14 @@ func (s *AuthenticationService) AcquireSessionCookie(username, password string)
return true, nil
}
// SetBasicAuth sets username and password for the basic auth against the JIRA instance.
// AcquireSessionCookie wraps AcquireSessionCookieWithContext using the background context.
//
// Deprecated: Use CookieAuthTransport instead
func (s *AuthenticationService) AcquireSessionCookie(username, password string) (bool, error) {
return s.AcquireSessionCookieWithContext(context.Background(), username, password)
}
// SetBasicAuth sets username and password for the basic auth against the Jira instance.
//
// Deprecated: Use BasicAuthTransport instead
func (s *AuthenticationService) SetBasicAuth(username, password string) {
@ -100,7 +108,7 @@ func (s *AuthenticationService) SetBasicAuth(username, password string) {
s.authType = authTypeBasic
}
// Authenticated reports if the current Client has authentication details for JIRA
// Authenticated reports if the current Client has authentication details for Jira
func (s *AuthenticationService) Authenticated() bool {
if s != nil {
if s.authType == authTypeSession {
@ -113,29 +121,30 @@ func (s *AuthenticationService) Authenticated() bool {
return false
}
// Logout logs out the current user that has been authenticated and the session in the client is destroyed.
// LogoutWithContext logs out the current user that has been authenticated and the session in the client is destroyed.
//
// JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#auth/1/session
// Jira API docs: https://docs.atlassian.com/jira/REST/latest/#auth/1/session
//
// Deprecated: Use CookieAuthTransport to create base client. Logging out is as simple as not using the
// client anymore
func (s *AuthenticationService) Logout() error {
func (s *AuthenticationService) LogoutWithContext(ctx context.Context) error {
if s.authType != authTypeSession || s.client.session == nil {
return fmt.Errorf("no user is authenticated")
}
apiEndpoint := "rest/auth/1/session"
req, err := s.client.NewRequest("DELETE", apiEndpoint, nil)
req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, nil)
if err != nil {
return fmt.Errorf("Creating the request to log the user out failed : %s", err)
return fmt.Errorf("creating the request to log the user out failed : %s", err)
}
resp, err := s.client.Do(req, nil)
if err != nil {
return fmt.Errorf("Error sending the logout request: %s", err)
return fmt.Errorf("error sending the logout request: %s", err)
}
defer resp.Body.Close()
if resp.StatusCode != 204 {
return fmt.Errorf("The logout was unsuccessful with status %d", resp.StatusCode)
return fmt.Errorf("the logout was unsuccessful with status %d", resp.StatusCode)
}
// If logout successful, delete session
@ -145,43 +154,55 @@ func (s *AuthenticationService) Logout() error {
}
// GetCurrentUser gets the details of the current user.
// Logout wraps LogoutWithContext using the background context.
//
// JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#auth/1/session
func (s *AuthenticationService) GetCurrentUser() (*Session, error) {
// Deprecated: Use CookieAuthTransport to create base client. Logging out is as simple as not using the
// client anymore
func (s *AuthenticationService) Logout() error {
return s.LogoutWithContext(context.Background())
}
// GetCurrentUserWithContext gets the details of the current user.
//
// Jira API docs: https://docs.atlassian.com/jira/REST/latest/#auth/1/session
func (s *AuthenticationService) GetCurrentUserWithContext(ctx context.Context) (*Session, error) {
if s == nil {
return nil, fmt.Errorf("AUthenticaiton Service is not instantiated")
return nil, fmt.Errorf("authentication Service is not instantiated")
}
if s.authType != authTypeSession || s.client.session == nil {
return nil, fmt.Errorf("No user is authenticated yet")
return nil, fmt.Errorf("no user is authenticated yet")
}
apiEndpoint := "rest/auth/1/session"
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, fmt.Errorf("Could not create request for getting user info : %s", err)
return nil, fmt.Errorf("could not create request for getting user info : %s", err)
}
resp, err := s.client.Do(req, nil)
if err != nil {
return nil, fmt.Errorf("Error sending request to get user info : %s", err)
return nil, fmt.Errorf("error sending request to get user info : %s", err)
}
if resp.StatusCode != 200 {
return nil, fmt.Errorf("Getting user info failed with status : %d", resp.StatusCode)
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return nil, fmt.Errorf("getting user info failed with status : %d", resp.StatusCode)
}
ret := new(Session)
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("Couldn't read body from the response : %s", err)
return nil, fmt.Errorf("couldn't read body from the response : %s", err)
}
err = json.Unmarshal(data, &ret)
if err != nil {
return nil, fmt.Errorf("Could not unmarshall received user info : %s", err)
return nil, fmt.Errorf("could not unmarshall received user info : %s", err)
}
return ret, nil
}
// GetCurrentUser wraps GetCurrentUserWithContext using the background context.
func (s *AuthenticationService) GetCurrentUser() (*Session, error) {
return s.GetCurrentUserWithContext(context.Background())
}

View file

@ -1,14 +1,15 @@
package jira
import (
"context"
"fmt"
"strconv"
"time"
)
// BoardService handles Agile Boards for the JIRA instance / API.
// BoardService handles Agile Boards for the Jira instance / API.
//
// JIRA API docs: https://docs.atlassian.com/jira-software/REST/server/
// Jira API docs: https://docs.atlassian.com/jira-software/REST/server/
type BoardService struct {
client *Client
}
@ -22,7 +23,7 @@ type BoardsList struct {
Values []Board `json:"values" structs:"values"`
}
// Board represents a JIRA agile board
// Board represents a Jira agile board
type Board struct {
ID int `json:"id,omitempty" structs:"id,omitempty"`
Self string `json:"self,omitempty" structs:"self,omitempty"`
@ -62,7 +63,7 @@ type SprintsList struct {
Values []Sprint `json:"values" structs:"values"`
}
// Sprint represents a sprint on JIRA agile board
// Sprint represents a sprint on Jira agile board
type Sprint struct {
ID int `json:"id" structs:"id"`
Name string `json:"name" structs:"name"`
@ -116,6 +117,8 @@ type BoardConfigurationColumnConfig struct {
type BoardConfigurationColumn struct {
Name string `json:"name"`
Status []BoardConfigurationColumnStatus `json:"statuses"`
Min int `json:"min,omitempty"`
Max int `json:"max,omitempty"`
}
// BoardConfigurationColumnStatus represents a status in the column configuration
@ -124,16 +127,16 @@ type BoardConfigurationColumnStatus struct {
Self string `json:"self"`
}
// GetAllBoards will returns all boards. This only includes boards that the user has permission to view.
// GetAllBoardsWithContext will returns all boards. This only includes boards that the user has permission to view.
//
// JIRA API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-getAllBoards
func (s *BoardService) GetAllBoards(opt *BoardListOptions) (*BoardsList, *Response, error) {
// Jira API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-getAllBoards
func (s *BoardService) GetAllBoardsWithContext(ctx context.Context, opt *BoardListOptions) (*BoardsList, *Response, error) {
apiEndpoint := "rest/agile/1.0/board"
url, err := addOptions(apiEndpoint, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", url, nil)
req, err := s.client.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, nil, err
}
@ -148,13 +151,18 @@ func (s *BoardService) GetAllBoards(opt *BoardListOptions) (*BoardsList, *Respon
return boards, resp, err
}
// GetBoard will returns the board for the given boardID.
// GetAllBoards wraps GetAllBoardsWithContext using the background context.
func (s *BoardService) GetAllBoards(opt *BoardListOptions) (*BoardsList, *Response, error) {
return s.GetAllBoardsWithContext(context.Background(), opt)
}
// GetBoardWithContext will returns the board for the given boardID.
// This board will only be returned if the user has permission to view it.
//
// JIRA API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-getBoard
func (s *BoardService) GetBoard(boardID int) (*Board, *Response, error) {
// Jira API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-getBoard
func (s *BoardService) GetBoardWithContext(ctx context.Context, boardID int) (*Board, *Response, error) {
apiEndpoint := fmt.Sprintf("rest/agile/1.0/board/%v", boardID)
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
@ -169,17 +177,22 @@ func (s *BoardService) GetBoard(boardID int) (*Board, *Response, error) {
return board, resp, nil
}
// CreateBoard creates a new board. Board name, type and filter Id is required.
// GetBoard wraps GetBoardWithContext using the background context.
func (s *BoardService) GetBoard(boardID int) (*Board, *Response, error) {
return s.GetBoardWithContext(context.Background(), boardID)
}
// CreateBoardWithContext creates a new board. Board name, type and filter Id is required.
// name - Must be less than 255 characters.
// type - Valid values: scrum, kanban
// filterId - Id of a filter that the user has permissions to view.
// Note, if the user does not have the 'Create shared objects' permission and tries to create a shared board, a private
// board will be created instead (remember that board sharing depends on the filter sharing).
//
// JIRA API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-createBoard
func (s *BoardService) CreateBoard(board *Board) (*Board, *Response, error) {
// Jira API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-createBoard
func (s *BoardService) CreateBoardWithContext(ctx context.Context, board *Board) (*Board, *Response, error) {
apiEndpoint := "rest/agile/1.0/board"
req, err := s.client.NewRequest("POST", apiEndpoint, board)
req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, board)
if err != nil {
return nil, nil, err
}
@ -194,12 +207,18 @@ func (s *BoardService) CreateBoard(board *Board) (*Board, *Response, error) {
return responseBoard, resp, nil
}
// DeleteBoard will delete an agile board.
// CreateBoard wraps CreateBoardWithContext using the background context.
func (s *BoardService) CreateBoard(board *Board) (*Board, *Response, error) {
return s.CreateBoardWithContext(context.Background(), board)
}
// DeleteBoardWithContext will delete an agile board.
//
// JIRA API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-deleteBoard
func (s *BoardService) DeleteBoard(boardID int) (*Board, *Response, error) {
// Jira API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-deleteBoard
// Caller must close resp.Body
func (s *BoardService) DeleteBoardWithContext(ctx context.Context, boardID int) (*Board, *Response, error) {
apiEndpoint := fmt.Sprintf("rest/agile/1.0/board/%v", boardID)
req, err := s.client.NewRequest("DELETE", apiEndpoint, nil)
req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
@ -211,11 +230,17 @@ func (s *BoardService) DeleteBoard(boardID int) (*Board, *Response, error) {
return nil, resp, err
}
// GetAllSprints will return all sprints from a board, for a given board Id.
// DeleteBoard wraps DeleteBoardWithContext using the background context.
// Caller must close resp.Body
func (s *BoardService) DeleteBoard(boardID int) (*Board, *Response, error) {
return s.DeleteBoardWithContext(context.Background(), boardID)
}
// GetAllSprintsWithContext will return all sprints from a board, for a given board Id.
// This only includes sprints that the user has permission to view.
//
// JIRA API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board/{boardId}/sprint
func (s *BoardService) GetAllSprints(boardID string) ([]Sprint, *Response, error) {
// Jira API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board/{boardId}/sprint
func (s *BoardService) GetAllSprintsWithContext(ctx context.Context, boardID string) ([]Sprint, *Response, error) {
id, err := strconv.Atoi(boardID)
if err != nil {
return nil, nil, err
@ -229,17 +254,22 @@ func (s *BoardService) GetAllSprints(boardID string) ([]Sprint, *Response, error
return result.Values, response, nil
}
// GetAllSprintsWithOptions will return sprints from a board, for a given board Id and filtering options
// GetAllSprints wraps GetAllSprintsWithContext using the background context.
func (s *BoardService) GetAllSprints(boardID string) ([]Sprint, *Response, error) {
return s.GetAllSprintsWithContext(context.Background(), boardID)
}
// GetAllSprintsWithOptionsWithContext will return sprints from a board, for a given board Id and filtering options
// This only includes sprints that the user has permission to view.
//
// JIRA API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board/{boardId}/sprint
func (s *BoardService) GetAllSprintsWithOptions(boardID int, options *GetAllSprintsOptions) (*SprintsList, *Response, error) {
// Jira API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board/{boardId}/sprint
func (s *BoardService) GetAllSprintsWithOptionsWithContext(ctx context.Context, boardID int, options *GetAllSprintsOptions) (*SprintsList, *Response, error) {
apiEndpoint := fmt.Sprintf("rest/agile/1.0/board/%d/sprint", boardID)
url, err := addOptions(apiEndpoint, options)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", url, nil)
req, err := s.client.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, nil, err
}
@ -253,12 +283,17 @@ func (s *BoardService) GetAllSprintsWithOptions(boardID int, options *GetAllSpri
return result, resp, err
}
// GetBoardConfiguration will return a board configuration for a given board Id
// GetAllSprintsWithOptions wraps GetAllSprintsWithOptionsWithContext using the background context.
func (s *BoardService) GetAllSprintsWithOptions(boardID int, options *GetAllSprintsOptions) (*SprintsList, *Response, error) {
return s.GetAllSprintsWithOptionsWithContext(context.Background(), boardID, options)
}
// GetBoardConfigurationWithContext will return a board configuration for a given board Id
// Jira API docs:https://developer.atlassian.com/cloud/jira/software/rest/#api-rest-agile-1-0-board-boardId-configuration-get
func (s *BoardService) GetBoardConfiguration(boardID int) (*BoardConfiguration, *Response, error) {
func (s *BoardService) GetBoardConfigurationWithContext(ctx context.Context, boardID int) (*BoardConfiguration, *Response, error) {
apiEndpoint := fmt.Sprintf("rest/agile/1.0/board/%d/configuration", boardID)
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
@ -273,3 +308,8 @@ func (s *BoardService) GetBoardConfiguration(boardID int) (*BoardConfiguration,
return result, resp, err
}
// GetBoardConfiguration wraps GetBoardConfigurationWithContext using the background context.
func (s *BoardService) GetBoardConfiguration(boardID int) (*BoardConfiguration, *Response, error) {
return s.GetBoardConfigurationWithContext(context.Background(), boardID)
}

View file

@ -1,13 +1,14 @@
package jira
// ComponentService handles components for the JIRA instance / API.
//
// JIRA API docs: https://docs.atlassian.com/software/jira/docs/api/REST/7.10.1/#api/2/component
import "context"
// ComponentService handles components for the Jira instance / API.//
// Jira API docs: https://docs.atlassian.com/software/jira/docs/api/REST/7.10.1/#api/2/component
type ComponentService struct {
client *Client
}
// CreateComponentOptions are passed to the ComponentService.Create function to create a new JIRA component
// CreateComponentOptions are passed to the ComponentService.Create function to create a new Jira component
type CreateComponentOptions struct {
Name string `json:"name,omitempty" structs:"name,omitempty"`
Description string `json:"description,omitempty" structs:"description,omitempty"`
@ -19,10 +20,10 @@ type CreateComponentOptions struct {
ProjectID int `json:"projectId,omitempty" structs:"projectId,omitempty"`
}
// Create creates a new JIRA component based on the given options.
func (s *ComponentService) Create(options *CreateComponentOptions) (*ProjectComponent, *Response, error) {
// CreateWithContext creates a new Jira component based on the given options.
func (s *ComponentService) CreateWithContext(ctx context.Context, options *CreateComponentOptions) (*ProjectComponent, *Response, error) {
apiEndpoint := "rest/api/2/component"
req, err := s.client.NewRequest("POST", apiEndpoint, options)
req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, options)
if err != nil {
return nil, nil, err
}
@ -36,3 +37,8 @@ func (s *ComponentService) Create(options *CreateComponentOptions) (*ProjectComp
return component, resp, nil
}
// Create wraps CreateWithContext using the background context.
func (s *ComponentService) Create(options *CreateComponentOptions) (*ProjectComponent, *Response, error) {
return s.CreateWithContext(context.Background(), options)
}

72
vendor/github.com/andygrunwald/go-jira/customer.go generated vendored Normal file
View file

@ -0,0 +1,72 @@
package jira
import (
"context"
"net/http"
)
// CustomerService handles ServiceDesk customers for the Jira instance / API.
type CustomerService struct {
client *Client
}
// Customer represents a ServiceDesk customer.
type Customer struct {
AccountID string `json:"accountId,omitempty" structs:"accountId,omitempty"`
Name string `json:"name,omitempty" structs:"name,omitempty"`
Key string `json:"key,omitempty" structs:"key,omitempty"`
EmailAddress string `json:"emailAddress,omitempty" structs:"emailAddress,omitempty"`
DisplayName string `json:"displayName,omitempty" structs:"displayName,omitempty"`
Active *bool `json:"active,omitempty" structs:"active,omitempty"`
TimeZone string `json:"timeZone,omitempty" structs:"timeZone,omitempty"`
Links *SelfLink `json:"_links,omitempty" structs:"_links,omitempty"`
}
// CustomerListOptions is the query options for listing customers.
type CustomerListOptions struct {
Query string `url:"query,omitempty"`
Start int `url:"start,omitempty"`
Limit int `url:"limit,omitempty"`
}
// CustomerList is a page of customers.
type CustomerList struct {
Values []Customer `json:"values,omitempty" structs:"values,omitempty"`
Start int `json:"start,omitempty" structs:"start,omitempty"`
Limit int `json:"limit,omitempty" structs:"limit,omitempty"`
IsLast bool `json:"isLastPage,omitempty" structs:"isLastPage,omitempty"`
Expands []string `json:"_expands,omitempty" structs:"_expands,omitempty"`
}
// CreateWithContext creates a ServiceDesk customer.
//
// https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-customer/#api-rest-servicedeskapi-customer-post
func (c *CustomerService) CreateWithContext(ctx context.Context, email, displayName string) (*Customer, *Response, error) {
const apiEndpoint = "rest/servicedeskapi/customer"
payload := struct {
Email string `json:"email"`
DisplayName string `json:"displayName"`
}{
Email: email,
DisplayName: displayName,
}
req, err := c.client.NewRequestWithContext(ctx, http.MethodPost, apiEndpoint, payload)
if err != nil {
return nil, nil, err
}
responseCustomer := new(Customer)
resp, err := c.client.Do(req, responseCustomer)
if err != nil {
return nil, resp, NewJiraError(resp, err)
}
return responseCustomer, resp, nil
}
// Create wraps CreateWithContext using the background context.
func (c *CustomerService) Create(email, displayName string) (*Customer, *Response, error) {
return c.CreateWithContext(context.Background(), email, displayName)
}

View file

@ -10,7 +10,7 @@ import (
"github.com/pkg/errors"
)
// Error message from JIRA
// Error message from Jira
// See https://docs.atlassian.com/jira/REST/cloud/#error-responses
type Error struct {
HTTPError error
@ -34,13 +34,13 @@ func NewJiraError(resp *Response, httpError error) error {
if strings.HasPrefix(contentType, "application/json") {
err = json.Unmarshal(body, &jerr)
if err != nil {
httpError = errors.Wrap(errors.New("Could not parse JSON"), httpError.Error())
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))
}
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)))
}

View file

@ -1,13 +1,15 @@
package jira
// FieldService handles fields for the JIRA instance / API.
import "context"
// FieldService handles fields for the Jira instance / API.
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-Field
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-Field
type FieldService struct {
client *Client
}
// Field represents a field of a JIRA issue.
// Field represents a field of a Jira issue.
type Field struct {
ID string `json:"id,omitempty" structs:"id,omitempty"`
Key string `json:"key,omitempty" structs:"key,omitempty"`
@ -19,17 +21,22 @@ type Field struct {
Schema FieldSchema `json:"schema,omitempty" structs:"schema,omitempty"`
}
// FieldSchema represents a schema of a Jira field.
// Documentation: https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issue-fields/#api-rest-api-2-field-get
type FieldSchema struct {
Type string `json:"type,omitempty" structs:"type,omitempty"`
System string `json:"system,omitempty" structs:"system,omitempty"`
Type string `json:"type,omitempty" structs:"type,omitempty"`
Items string `json:"items,omitempty" structs:"items,omitempty"`
Custom string `json:"custom,omitempty" structs:"custom,omitempty"`
System string `json:"system,omitempty" structs:"system,omitempty"`
CustomID int64 `json:"customId,omitempty" structs:"customId,omitempty"`
}
// GetList gets all fields from JIRA
// GetListWithContext gets all fields from Jira
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-field-get
func (s *FieldService) GetList() ([]Field, *Response, error) {
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-field-get
func (s *FieldService) GetListWithContext(ctx context.Context) ([]Field, *Response, error) {
apiEndpoint := "rest/api/2/field"
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
@ -41,3 +48,8 @@ func (s *FieldService) GetList() ([]Field, *Response, error) {
}
return fieldList, resp, nil
}
// GetList wraps GetListWithContext using the background context.
func (s *FieldService) GetList() ([]Field, *Response, error) {
return s.GetListWithContext(context.Background())
}

View file

@ -1,11 +1,15 @@
package jira
import "github.com/google/go-querystring/query"
import "fmt"
import (
"context"
"fmt"
// FilterService handles fields for the JIRA instance / API.
"github.com/google/go-querystring/query"
)
// FilterService handles fields for the Jira instance / API.
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-group-Filter
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-group-Filter
type FilterService struct {
client *Client
}
@ -116,23 +120,21 @@ type FilterSearchOptions struct {
Expand string `url:"expand,omitempty"`
}
// GetList retrieves all filters from Jira
func (fs *FilterService) GetList() ([]*Filter, *Response, error) {
// GetListWithContext retrieves all filters from Jira
func (fs *FilterService) GetListWithContext(ctx context.Context) ([]*Filter, *Response, error) {
options := &GetQueryOptions{}
apiEndpoint := "rest/api/2/filter"
req, err := fs.client.NewRequest("GET", apiEndpoint, nil)
req, err := fs.client.NewRequestWithContext(ctx, "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()
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)
@ -143,10 +145,15 @@ func (fs *FilterService) GetList() ([]*Filter, *Response, error) {
return filters, resp, err
}
// GetFavouriteList retrieves the user's favourited filters from Jira
func (fs *FilterService) GetFavouriteList() ([]*Filter, *Response, error) {
// GetList wraps GetListWithContext using the background context.
func (fs *FilterService) GetList() ([]*Filter, *Response, error) {
return fs.GetListWithContext(context.Background())
}
// GetFavouriteListWithContext retrieves the user's favourited filters from Jira
func (fs *FilterService) GetFavouriteListWithContext(ctx context.Context) ([]*Filter, *Response, error) {
apiEndpoint := "rest/api/2/filter/favourite"
req, err := fs.client.NewRequest("GET", apiEndpoint, nil)
req, err := fs.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
@ -159,10 +166,15 @@ func (fs *FilterService) GetFavouriteList() ([]*Filter, *Response, error) {
return filters, resp, err
}
// Get retrieves a single Filter from Jira
func (fs *FilterService) Get(filterID int) (*Filter, *Response, error) {
// GetFavouriteList wraps GetFavouriteListWithContext using the background context.
func (fs *FilterService) GetFavouriteList() ([]*Filter, *Response, error) {
return fs.GetFavouriteListWithContext(context.Background())
}
// GetWithContext retrieves a single Filter from Jira
func (fs *FilterService) GetWithContext(ctx context.Context, filterID int) (*Filter, *Response, error) {
apiEndpoint := fmt.Sprintf("rest/api/2/filter/%d", filterID)
req, err := fs.client.NewRequest("GET", apiEndpoint, nil)
req, err := fs.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
@ -176,16 +188,21 @@ func (fs *FilterService) Get(filterID int) (*Filter, *Response, error) {
return filter, resp, err
}
// GetMyFilters retrieves the my Filters.
// Get wraps GetWithContext using the background context.
func (fs *FilterService) Get(filterID int) (*Filter, *Response, error) {
return fs.GetWithContext(context.Background(), filterID)
}
// GetMyFiltersWithContext retrieves the my Filters.
//
// https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-rest-api-3-filter-my-get
func (fs *FilterService) GetMyFilters(opts *GetMyFiltersQueryOptions) ([]*Filter, *Response, error) {
func (fs *FilterService) GetMyFiltersWithContext(ctx context.Context, opts *GetMyFiltersQueryOptions) ([]*Filter, *Response, error) {
apiEndpoint := "rest/api/3/filter/my"
url, err := addOptions(apiEndpoint, opts)
if err != nil {
return nil, nil, err
}
req, err := fs.client.NewRequest("GET", url, nil)
req, err := fs.client.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, nil, err
}
@ -199,16 +216,21 @@ func (fs *FilterService) GetMyFilters(opts *GetMyFiltersQueryOptions) ([]*Filter
return filters, resp, nil
}
// Search will search for filter according to the search options
// GetMyFilters wraps GetMyFiltersWithContext using the background context.
func (fs *FilterService) GetMyFilters(opts *GetMyFiltersQueryOptions) ([]*Filter, *Response, error) {
return fs.GetMyFiltersWithContext(context.Background(), opts)
}
// SearchWithContext will search for filter according to the search options
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-rest-api-3-filter-search-get
func (fs *FilterService) Search(opt *FilterSearchOptions) (*FiltersList, *Response, error) {
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-rest-api-3-filter-search-get
func (fs *FilterService) SearchWithContext(ctx context.Context, opt *FilterSearchOptions) (*FiltersList, *Response, error) {
apiEndpoint := "rest/api/3/filter/search"
url, err := addOptions(apiEndpoint, opt)
if err != nil {
return nil, nil, err
}
req, err := fs.client.NewRequest("GET", url, nil)
req, err := fs.client.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, nil, err
}
@ -222,3 +244,8 @@ func (fs *FilterService) Search(opt *FilterSearchOptions) (*FiltersList, *Respon
return filters, resp, err
}
// Search wraps SearchWithContext using the background context.
func (fs *FilterService) Search(opt *FilterSearchOptions) (*FiltersList, *Response, error) {
return fs.SearchWithContext(context.Background(), opt)
}

View file

@ -1,13 +1,14 @@
package jira
import (
"context"
"fmt"
"net/url"
)
// GroupService handles Groups for the JIRA instance / API.
// GroupService handles Groups for the Jira instance / API.
//
// JIRA API docs: https://docs.atlassian.com/jira/REST/server/#api/2/group
// Jira API docs: https://docs.atlassian.com/jira/REST/server/#api/2/group
type GroupService struct {
client *Client
}
@ -21,7 +22,7 @@ type groupMembersResult struct {
Members []GroupMember `json:"values"`
}
// Group represents a JIRA group
// Group represents a Jira group
type Group struct {
ID string `json:"id"`
Title string `json:"title"`
@ -58,16 +59,16 @@ type GroupSearchOptions struct {
IncludeInactiveUsers bool
}
// Get returns a paginated list of users who are members of the specified group and its subgroups.
// GetWithContext returns a paginated list of users who are members of the specified group and its subgroups.
// Users in the page are ordered by user names.
// User of this resource is required to have sysadmin or admin permissions.
//
// JIRA API docs: https://docs.atlassian.com/jira/REST/server/#api/2/group-getUsersFromGroup
// Jira API docs: https://docs.atlassian.com/jira/REST/server/#api/2/group-getUsersFromGroup
//
// WARNING: This API only returns the first page of group members
func (s *GroupService) Get(name string) ([]GroupMember, *Response, error) {
func (s *GroupService) GetWithContext(ctx context.Context, name string) ([]GroupMember, *Response, error) {
apiEndpoint := fmt.Sprintf("/rest/api/2/group/member?groupname=%s", url.QueryEscape(name))
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
@ -81,12 +82,17 @@ func (s *GroupService) Get(name string) ([]GroupMember, *Response, error) {
return group.Members, resp, nil
}
// GetWithOptions returns a paginated list of members of the specified group and its subgroups.
// Get wraps GetWithContext using the background context.
func (s *GroupService) Get(name string) ([]GroupMember, *Response, error) {
return s.GetWithContext(context.Background(), name)
}
// GetWithOptionsWithContext returns a paginated list of members of the specified group and its subgroups.
// Users in the page are ordered by user names.
// User of this resource is required to have sysadmin or admin permissions.
//
// JIRA API docs: https://docs.atlassian.com/jira/REST/server/#api/2/group-getUsersFromGroup
func (s *GroupService) GetWithOptions(name string, options *GroupSearchOptions) ([]GroupMember, *Response, error) {
// Jira API docs: https://docs.atlassian.com/jira/REST/server/#api/2/group-getUsersFromGroup
func (s *GroupService) GetWithOptionsWithContext(ctx context.Context, name string, options *GroupSearchOptions) ([]GroupMember, *Response, error) {
var apiEndpoint string
if options == nil {
apiEndpoint = fmt.Sprintf("/rest/api/2/group/member?groupname=%s", url.QueryEscape(name))
@ -99,7 +105,7 @@ func (s *GroupService) GetWithOptions(name string, options *GroupSearchOptions)
options.IncludeInactiveUsers,
)
}
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
@ -112,16 +118,21 @@ func (s *GroupService) GetWithOptions(name string, options *GroupSearchOptions)
return group.Members, resp, nil
}
// Add adds user to group
// GetWithOptions wraps GetWithOptionsWithContext using the background context.
func (s *GroupService) GetWithOptions(name string, options *GroupSearchOptions) ([]GroupMember, *Response, error) {
return s.GetWithOptionsWithContext(context.Background(), name, options)
}
// AddWithContext adds user to group
//
// JIRA API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/group-addUserToGroup
func (s *GroupService) Add(groupname string, username string) (*Group, *Response, error) {
// Jira API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/group-addUserToGroup
func (s *GroupService) AddWithContext(ctx context.Context, groupname string, username string) (*Group, *Response, error) {
apiEndpoint := fmt.Sprintf("/rest/api/2/group/user?groupname=%s", groupname)
var user struct {
Name string `json:"name"`
}
user.Name = username
req, err := s.client.NewRequest("POST", apiEndpoint, &user)
req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, &user)
if err != nil {
return nil, nil, err
}
@ -136,12 +147,18 @@ func (s *GroupService) Add(groupname string, username string) (*Group, *Response
return responseGroup, resp, nil
}
// Remove removes user from group
// Add wraps AddWithContext using the background context.
func (s *GroupService) Add(groupname string, username string) (*Group, *Response, error) {
return s.AddWithContext(context.Background(), groupname, username)
}
// RemoveWithContext removes user from group
//
// JIRA API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/group-removeUserFromGroup
func (s *GroupService) Remove(groupname string, username string) (*Response, error) {
// Jira API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/group-removeUserFromGroup
// Caller must close resp.Body
func (s *GroupService) RemoveWithContext(ctx context.Context, groupname string, username string) (*Response, error) {
apiEndpoint := fmt.Sprintf("/rest/api/2/group/user?groupname=%s&username=%s", groupname, username)
req, err := s.client.NewRequest("DELETE", apiEndpoint, nil)
req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, nil)
if err != nil {
return nil, err
}
@ -154,3 +171,9 @@ func (s *GroupService) Remove(groupname string, username string) (*Response, err
return resp, nil
}
// Remove wraps RemoveWithContext using the background context.
// Caller must close resp.Body
func (s *GroupService) Remove(groupname string, username string) (*Response, error) {
return s.RemoveWithContext(context.Background(), groupname, username)
}

File diff suppressed because it is too large Load diff

View file

@ -1,24 +1,25 @@
package jira
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
)
// IssueLinkTypeService handles issue link types for the JIRA instance / API.
// IssueLinkTypeService handles issue link types for the Jira instance / API.
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-group-Issue-link-types
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-group-Issue-link-types
type IssueLinkTypeService struct {
client *Client
}
// GetList gets all of the issue link types from JIRA.
// GetListWithContext gets all of the issue link types from Jira.
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issueLinkType-get
func (s *IssueLinkTypeService) GetList() ([]IssueLinkType, *Response, error) {
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issueLinkType-get
func (s *IssueLinkTypeService) GetListWithContext(ctx context.Context) ([]IssueLinkType, *Response, error) {
apiEndpoint := "rest/api/2/issueLinkType"
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
@ -31,12 +32,17 @@ func (s *IssueLinkTypeService) GetList() ([]IssueLinkType, *Response, error) {
return linkTypeList, resp, nil
}
// Get gets info of a specific issue link type from JIRA.
// GetList wraps GetListWithContext using the background context.
func (s *IssueLinkTypeService) GetList() ([]IssueLinkType, *Response, error) {
return s.GetListWithContext(context.Background())
}
// GetWithContext gets info of a specific issue link type from Jira.
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issueLinkType-issueLinkTypeId-get
func (s *IssueLinkTypeService) Get(ID string) (*IssueLinkType, *Response, error) {
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issueLinkType-issueLinkTypeId-get
func (s *IssueLinkTypeService) GetWithContext(ctx context.Context, ID string) (*IssueLinkType, *Response, error) {
apiEndPoint := fmt.Sprintf("rest/api/2/issueLinkType/%s", ID)
req, err := s.client.NewRequest("GET", apiEndPoint, nil)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndPoint, nil)
if err != nil {
return nil, nil, err
}
@ -49,12 +55,17 @@ func (s *IssueLinkTypeService) Get(ID string) (*IssueLinkType, *Response, error)
return linkType, resp, nil
}
// Create creates an issue link type in JIRA.
// Get wraps GetWithContext using the background context.
func (s *IssueLinkTypeService) Get(ID string) (*IssueLinkType, *Response, error) {
return s.GetWithContext(context.Background(), ID)
}
// CreateWithContext creates an issue link type in Jira.
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issueLinkType-post
func (s *IssueLinkTypeService) Create(linkType *IssueLinkType) (*IssueLinkType, *Response, error) {
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issueLinkType-post
func (s *IssueLinkTypeService) CreateWithContext(ctx context.Context, linkType *IssueLinkType) (*IssueLinkType, *Response, error) {
apiEndpoint := "/rest/api/2/issueLinkType"
req, err := s.client.NewRequest("POST", apiEndpoint, linkType)
req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, linkType)
if err != nil {
return nil, nil, err
}
@ -68,23 +79,29 @@ func (s *IssueLinkTypeService) Create(linkType *IssueLinkType) (*IssueLinkType,
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
e := fmt.Errorf("Could not read the returned data")
e := fmt.Errorf("could not read the returned data")
return nil, resp, NewJiraError(resp, e)
}
err = json.Unmarshal(data, responseLinkType)
if err != nil {
e := fmt.Errorf("Could no unmarshal the data into struct")
e := fmt.Errorf("could no unmarshal the data into struct")
return nil, resp, NewJiraError(resp, e)
}
return linkType, resp, nil
}
// Update updates an issue link type. The issue is found by key.
// Create wraps CreateWithContext using the background context.
func (s *IssueLinkTypeService) Create(linkType *IssueLinkType) (*IssueLinkType, *Response, error) {
return s.CreateWithContext(context.Background(), linkType)
}
// UpdateWithContext updates an issue link type. The issue is found by key.
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issueLinkType-issueLinkTypeId-put
func (s *IssueLinkTypeService) Update(linkType *IssueLinkType) (*IssueLinkType, *Response, error) {
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issueLinkType-issueLinkTypeId-put
// Caller must close resp.Body
func (s *IssueLinkTypeService) UpdateWithContext(ctx context.Context, linkType *IssueLinkType) (*IssueLinkType, *Response, error) {
apiEndpoint := fmt.Sprintf("rest/api/2/issueLinkType/%s", linkType.ID)
req, err := s.client.NewRequest("PUT", apiEndpoint, linkType)
req, err := s.client.NewRequestWithContext(ctx, "PUT", apiEndpoint, linkType)
if err != nil {
return nil, nil, err
}
@ -96,12 +113,19 @@ func (s *IssueLinkTypeService) Update(linkType *IssueLinkType) (*IssueLinkType,
return &ret, resp, nil
}
// Delete deletes an issue link type based on provided ID.
// Update wraps UpdateWithContext using the background context.
// Caller must close resp.Body
func (s *IssueLinkTypeService) Update(linkType *IssueLinkType) (*IssueLinkType, *Response, error) {
return s.UpdateWithContext(context.Background(), linkType)
}
// DeleteWithContext deletes an issue link type based on provided ID.
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issueLinkType-issueLinkTypeId-delete
func (s *IssueLinkTypeService) Delete(ID string) (*Response, error) {
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issueLinkType-issueLinkTypeId-delete
// Caller must close resp.Body
func (s *IssueLinkTypeService) DeleteWithContext(ctx context.Context, ID string) (*Response, error) {
apiEndpoint := fmt.Sprintf("rest/api/2/issueLinkType/%s", ID)
req, err := s.client.NewRequest("DELETE", apiEndpoint, nil)
req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, nil)
if err != nil {
return nil, err
}
@ -109,3 +133,9 @@ func (s *IssueLinkTypeService) Delete(ID string) (*Response, error) {
resp, err := s.client.Do(req, nil)
return resp, err
}
// Delete wraps DeleteWithContext using the background context.
// Caller must close resp.Body
func (s *IssueLinkTypeService) Delete(ID string) (*Response, error) {
return s.DeleteWithContext(context.Background(), ID)
}

View file

@ -2,6 +2,7 @@ package jira
import (
"bytes"
"context"
"crypto/sha256"
"encoding/hex"
"encoding/json"
@ -14,7 +15,7 @@ import (
"strings"
"time"
"github.com/dgrijalva/jwt-go"
jwt "github.com/golang-jwt/jwt/v4"
"github.com/google/go-querystring/query"
"github.com/pkg/errors"
)
@ -25,7 +26,7 @@ type httpClient interface {
Do(request *http.Request) (response *http.Response, err error)
}
// A Client manages communication with the JIRA API.
// A Client manages communication with the Jira API.
type Client struct {
// HTTP client used to communicate with the API.
client httpClient
@ -36,7 +37,7 @@ type Client struct {
// Session storage if the user authenticates with a Session cookie
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
Issue *IssueService
Project *ProjectService
@ -55,15 +56,19 @@ type Client struct {
PermissionScheme *PermissionSchemeService
Status *StatusService
IssueLinkType *IssueLinkTypeService
Organization *OrganizationService
ServiceDesk *ServiceDeskService
Customer *CustomerService
Request *RequestService
}
// NewClient returns a new JIRA API client.
// NewClient returns a new Jira API client.
// If a nil httpClient is provided, http.DefaultClient will be used.
// To use API methods which require authentication you can follow the preferred solution and
// provide an http.Client that will perform the authentication for you with OAuth and HTTP Basic (such as that provided by the golang.org/x/oauth2 library).
// As an alternative you can use Session Cookie based authentication provided by this package as well.
// See https://docs.atlassian.com/jira/REST/latest/#authentication
// baseURL is the HTTP endpoint of your JIRA instance and should always be specified with a trailing slash.
// baseURL is the HTTP endpoint of your Jira instance and should always be specified with a trailing slash.
func NewClient(httpClient httpClient, baseURL string) (*Client, error) {
if httpClient == nil {
httpClient = http.DefaultClient
@ -101,14 +106,18 @@ func NewClient(httpClient httpClient, baseURL string) (*Client, error) {
c.PermissionScheme = &PermissionSchemeService{client: c}
c.Status = &StatusService{client: c}
c.IssueLinkType = &IssueLinkTypeService{client: c}
c.Organization = &OrganizationService{client: c}
c.ServiceDesk = &ServiceDeskService{client: c}
c.Customer = &CustomerService{client: c}
c.Request = &RequestService{client: c}
return c, nil
}
// NewRawRequest creates an API request.
// NewRawRequestWithContext creates an API request.
// A relative URL can be provided in urlStr, in which case it is resolved relative to the baseURL of the Client.
// Allows using an optional native io.Reader for sourcing the request body.
func (c *Client) NewRawRequest(method, urlStr string, body io.Reader) (*http.Request, error) {
func (c *Client) NewRawRequestWithContext(ctx context.Context, method, urlStr string, body io.Reader) (*http.Request, error) {
rel, err := url.Parse(urlStr)
if err != nil {
return nil, err
@ -118,7 +127,7 @@ func (c *Client) NewRawRequest(method, urlStr string, body io.Reader) (*http.Req
u := c.baseURL.ResolveReference(rel)
req, err := http.NewRequest(method, u.String(), body)
req, err := newRequestWithContext(ctx, method, u.String(), body)
if err != nil {
return nil, err
}
@ -143,10 +152,15 @@ func (c *Client) NewRawRequest(method, urlStr string, body io.Reader) (*http.Req
return req, nil
}
// NewRequest creates an API request.
// NewRawRequest wraps NewRawRequestWithContext using the background context.
func (c *Client) NewRawRequest(method, urlStr string, body io.Reader) (*http.Request, error) {
return c.NewRawRequestWithContext(context.Background(), method, urlStr, body)
}
// NewRequestWithContext creates an API request.
// A relative URL can be provided in urlStr, in which case it is resolved relative to the baseURL of the Client.
// If specified, the value pointed to by body is JSON encoded and included as the request body.
func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) {
func (c *Client) NewRequestWithContext(ctx context.Context, method, urlStr string, body interface{}) (*http.Request, error) {
rel, err := url.Parse(urlStr)
if err != nil {
return nil, err
@ -165,7 +179,7 @@ func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Requ
}
}
req, err := http.NewRequest(method, u.String(), buf)
req, err := newRequestWithContext(ctx, method, u.String(), buf)
if err != nil {
return nil, err
}
@ -190,6 +204,11 @@ func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Requ
return req, nil
}
// NewRequest wraps NewRequestWithContext using the background context.
func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) {
return c.NewRequestWithContext(context.Background(), method, urlStr, body)
}
// addOptions adds the parameters in opt as URL query parameters to s. opt
// must be a struct whose fields may contain "url" tags.
func addOptions(s string, opt interface{}) (string, error) {
@ -212,10 +231,10 @@ func addOptions(s string, opt interface{}) (string, error) {
return u.String(), nil
}
// NewMultiPartRequest creates an API request including a multi-part file.
// NewMultiPartRequestWithContext creates an API request including a multi-part file.
// A relative URL can be provided in urlStr, in which case it is resolved relative to the baseURL of the Client.
// If specified, the value pointed to by buf is a multipart form.
func (c *Client) NewMultiPartRequest(method, urlStr string, buf *bytes.Buffer) (*http.Request, error) {
func (c *Client) NewMultiPartRequestWithContext(ctx context.Context, method, urlStr string, buf *bytes.Buffer) (*http.Request, error) {
rel, err := url.Parse(urlStr)
if err != nil {
return nil, err
@ -225,7 +244,7 @@ func (c *Client) NewMultiPartRequest(method, urlStr string, buf *bytes.Buffer) (
u := c.baseURL.ResolveReference(rel)
req, err := http.NewRequest(method, u.String(), buf)
req, err := newRequestWithContext(ctx, method, u.String(), buf)
if err != nil {
return nil, err
}
@ -251,6 +270,11 @@ func (c *Client) NewMultiPartRequest(method, urlStr string, buf *bytes.Buffer) (
return req, nil
}
// NewMultiPartRequest wraps NewMultiPartRequestWithContext using the background context.
func (c *Client) NewMultiPartRequest(method, urlStr string, buf *bytes.Buffer) (*http.Request, error) {
return c.NewMultiPartRequestWithContext(context.Background(), method, urlStr, buf)
}
// Do sends an API request and returns the API response.
// The API response is JSON decoded and stored in the value pointed to by v, or returned as an error if an API error has occurred.
func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
@ -279,13 +303,13 @@ func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
// CheckResponse checks the API response for errors, and returns them if present.
// A response is considered an error if it has a status code outside the 200 range.
// The caller is responsible to analyze the response body.
// The body can contain JSON (if the error is intended) or xml (sometimes JIRA just failes).
// The body can contain JSON (if the error is intended) or xml (sometimes Jira just failes).
func CheckResponse(r *http.Response) error {
if c := r.StatusCode; 200 <= c && c <= 299 {
return nil
}
err := fmt.Errorf("Request failed. Please analyze the request body for more details. Status code: %d", r.StatusCode)
err := fmt.Errorf("request failed. Please analyze the request body for more details. Status code: %d", r.StatusCode)
return err
}
@ -295,7 +319,7 @@ func (c *Client) GetBaseURL() url.URL {
return *c.baseURL
}
// Response represents JIRA API response. It wraps http.Response returned from
// Response represents Jira API response. It wraps http.Response returned from
// API and provides information about paging.
type Response struct {
*http.Response
@ -324,7 +348,6 @@ func (r *Response) populatePageValues(v interface{}) {
r.MaxResults = value.MaxResults
r.Total = value.Total
}
return
}
// BasicAuthTransport is an http.RoundTripper that authenticates all requests
@ -363,13 +386,84 @@ func (t *BasicAuthTransport) transport() http.RoundTripper {
return http.DefaultTransport
}
// BearerAuthTransport is a http.RoundTripper that authenticates all requests
// using Jira's bearer (oauth 2.0 (3lo)) based authentication.
type BearerAuthTransport struct {
Token string
// Transport is the underlying HTTP transport to use when making requests.
// It will default to http.DefaultTransport if nil.
Transport http.RoundTripper
}
// RoundTrip implements the RoundTripper interface. We just add the
// bearer token and return the RoundTripper for this transport type.
func (t *BearerAuthTransport) RoundTrip(req *http.Request) (*http.Response, error) {
req2 := cloneRequest(req) // per RoundTripper contract
req2.Header.Set("Authorization", fmt.Sprintf("Bearer %s", t.Token))
return t.transport().RoundTrip(req2)
}
// Client returns an *http.Client that makes requests that are authenticated
// using HTTP Basic Authentication. This is a nice little bit of sugar
// so we can just get the client instead of creating the client in the calling code.
// If it's necessary to send more information on client init, the calling code can
// always skip this and set the transport itself.
func (t *BearerAuthTransport) Client() *http.Client {
return &http.Client{Transport: t}
}
func (t *BearerAuthTransport) transport() http.RoundTripper {
if t.Transport != nil {
return t.Transport
}
return http.DefaultTransport
}
// PATAuthTransport is an http.RoundTripper that authenticates all requests
// using the Personal Access Token specified.
// See here for more info: https://confluence.atlassian.com/enterprise/using-personal-access-tokens-1026032365.html
type PATAuthTransport struct {
// Token is the key that was provided by Jira when creating the Personal Access Token.
Token string
// Transport is the underlying HTTP transport to use when making requests.
// It will default to http.DefaultTransport if nil.
Transport http.RoundTripper
}
// RoundTrip implements the RoundTripper interface. We just add the
// basic auth and return the RoundTripper for this transport type.
func (t *PATAuthTransport) RoundTrip(req *http.Request) (*http.Response, error) {
req2 := cloneRequest(req) // per RoundTripper contract
req2.Header.Set("Authorization", "Bearer "+t.Token)
return t.transport().RoundTrip(req2)
}
// Client returns an *http.Client that makes requests that are authenticated
// using HTTP Basic Authentication. This is a nice little bit of sugar
// so we can just get the client instead of creating the client in the calling code.
// If it's necessary to send more information on client init, the calling code can
// always skip this and set the transport itself.
func (t *PATAuthTransport) Client() *http.Client {
return &http.Client{Transport: t}
}
func (t *PATAuthTransport) transport() http.RoundTripper {
if t.Transport != nil {
return t.Transport
}
return http.DefaultTransport
}
// CookieAuthTransport is an http.RoundTripper that authenticates all requests
// using Jira's cookie-based authentication.
//
// Note that it is generally preferable to use HTTP BASIC authentication with the REST API.
// However, this resource may be used to mimic the behaviour of JIRA's log-in page (e.g. to display log-in errors to a user).
// However, this resource may be used to mimic the behaviour of Jira's log-in page (e.g. to display log-in errors to a user).
//
// JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#auth/1/session
// Jira API docs: https://docs.atlassian.com/jira/REST/latest/#auth/1/session
type CookieAuthTransport struct {
Username string
Password string
@ -425,6 +519,7 @@ func (t *CookieAuthTransport) setSessionObject() error {
if err != nil {
return err
}
defer resp.Body.Close()
t.SessionObject = resp.Cookies()
return nil
@ -464,7 +559,7 @@ func (t *CookieAuthTransport) transport() http.RoundTripper {
//
// NOTE: this form of auth should be used by add-ons installed from the Atlassian marketplace.
//
// JIRA docs: https://developer.atlassian.com/cloud/jira/platform/understanding-jwt
// Jira docs: https://developer.atlassian.com/cloud/jira/platform/understanding-jwt
// Examples in other languages:
// https://bitbucket.org/atlassian/atlassian-jwt-ruby/src/d44a8e7a4649e4f23edaa784402655fda7c816ea/lib/atlassian/jwt.rb
// https://bitbucket.org/atlassian/atlassian-jwt-py/src/master/atlassian_jwt/url_utils.py

View file

@ -1,6 +1,7 @@
package jira
import (
"context"
"fmt"
"strings"
@ -14,6 +15,11 @@ type CreateMetaInfo struct {
Projects []*MetaProject `json:"projects,omitempty"`
}
// EditMetaInfo contains information about fields and their attributed to edit a ticket.
type EditMetaInfo struct {
Fields tcontainer.MarshalMap `json:"fields,omitempty"`
}
// MetaProject is the meta information about a project returned from createmeta api
type MetaProject struct {
Expand string `json:"expand,omitempty"`
@ -42,16 +48,21 @@ type MetaIssueType struct {
Fields tcontainer.MarshalMap `json:"fields,omitempty"`
}
// GetCreateMeta makes the api call to get the meta information required to create a ticket
func (s *IssueService) GetCreateMeta(projectkeys string) (*CreateMetaInfo, *Response, error) {
return s.GetCreateMetaWithOptions(&GetQueryOptions{ProjectKeys: projectkeys, Expand: "projects.issuetypes.fields"})
// GetCreateMetaWithContext makes the api call to get the meta information required to create a ticket
func (s *IssueService) GetCreateMetaWithContext(ctx context.Context, projectkeys string) (*CreateMetaInfo, *Response, error) {
return s.GetCreateMetaWithOptionsWithContext(ctx, &GetQueryOptions{ProjectKeys: projectkeys, Expand: "projects.issuetypes.fields"})
}
// GetCreateMetaWithOptions makes the api call to get the meta information without requiring to have a projectKey
func (s *IssueService) GetCreateMetaWithOptions(options *GetQueryOptions) (*CreateMetaInfo, *Response, error) {
// GetCreateMeta wraps GetCreateMetaWithContext using the background context.
func (s *IssueService) GetCreateMeta(projectkeys string) (*CreateMetaInfo, *Response, error) {
return s.GetCreateMetaWithContext(context.Background(), projectkeys)
}
// GetCreateMetaWithOptionsWithContext makes the api call to get the meta information without requiring to have a projectKey
func (s *IssueService) GetCreateMetaWithOptionsWithContext(ctx context.Context, options *GetQueryOptions) (*CreateMetaInfo, *Response, error) {
apiEndpoint := "rest/api/2/issue/createmeta"
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
@ -73,11 +84,40 @@ func (s *IssueService) GetCreateMetaWithOptions(options *GetQueryOptions) (*Crea
return meta, resp, nil
}
// GetCreateMetaWithOptions wraps GetCreateMetaWithOptionsWithContext using the background context.
func (s *IssueService) GetCreateMetaWithOptions(options *GetQueryOptions) (*CreateMetaInfo, *Response, error) {
return s.GetCreateMetaWithOptionsWithContext(context.Background(), options)
}
// GetEditMetaWithContext makes the api call to get the edit meta information for an issue
func (s *IssueService) GetEditMetaWithContext(ctx context.Context, issue *Issue) (*EditMetaInfo, *Response, error) {
apiEndpoint := fmt.Sprintf("/rest/api/2/issue/%s/editmeta", issue.Key)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
meta := new(EditMetaInfo)
resp, err := s.client.Do(req, meta)
if err != nil {
return nil, resp, err
}
return meta, resp, nil
}
// GetEditMeta wraps GetEditMetaWithContext using the background context.
func (s *IssueService) GetEditMeta(issue *Issue) (*EditMetaInfo, *Response, error) {
return s.GetEditMetaWithContext(context.Background(), issue)
}
// GetProjectWithName returns a project with "name" from the meta information received. If not found, this returns nil.
// The comparison of the name is case insensitive.
func (m *CreateMetaInfo) GetProjectWithName(name string) *MetaProject {
for _, m := range m.Projects {
if strings.ToLower(m.Name) == strings.ToLower(name) {
if strings.EqualFold(m.Name, name) {
return m
}
}
@ -88,7 +128,7 @@ func (m *CreateMetaInfo) GetProjectWithName(name string) *MetaProject {
// The comparison of the name is case insensitive.
func (m *CreateMetaInfo) GetProjectWithKey(key string) *MetaProject {
for _, m := range m.Projects {
if strings.ToLower(m.Key) == strings.ToLower(key) {
if strings.EqualFold(m.Key, key) {
return m
}
}
@ -99,7 +139,7 @@ func (m *CreateMetaInfo) GetProjectWithKey(key string) *MetaProject {
// The comparison of the name is case insensitive
func (p *MetaProject) GetIssueTypeWithName(name string) *MetaIssueType {
for _, m := range p.IssueTypes {
if strings.ToLower(m.Name) == strings.ToLower(name) {
if strings.EqualFold(m.Name, name) {
return m
}
}
@ -175,7 +215,7 @@ func (t *MetaIssueType) CheckCompleteAndAvailable(config map[string]string) (boo
for name := range mandatory {
requiredFields = append(requiredFields, name)
}
return false, fmt.Errorf("Required field not found in provided jira.fields. Required are: %#v", requiredFields)
return false, fmt.Errorf("required field not found in provided jira.fields. Required are: %#v", requiredFields)
}
}
@ -186,7 +226,7 @@ func (t *MetaIssueType) CheckCompleteAndAvailable(config map[string]string) (boo
for name := range all {
availableFields = append(availableFields, name)
}
return false, fmt.Errorf("Fields in jira.fields are not available in jira. Available are: %#v", availableFields)
return false, fmt.Errorf("fields in jira.fields are not available in jira. Available are: %#v", availableFields)
}
}

397
vendor/github.com/andygrunwald/go-jira/organization.go generated vendored Normal file
View file

@ -0,0 +1,397 @@
package jira
import (
"context"
"fmt"
)
// OrganizationService handles Organizations for the Jira instance / API.
//
// Jira API docs: https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-organization/
type OrganizationService struct {
client *Client
}
// OrganizationCreationDTO is DTO for creat organization API
type OrganizationCreationDTO struct {
Name string `json:"name,omitempty" structs:"name,omitempty"`
}
// SelfLink Stores REST API URL to the organization.
type SelfLink struct {
Self string `json:"self,omitempty" structs:"self,omitempty"`
}
// Organization contains Organization data
type Organization struct {
ID string `json:"id,omitempty" structs:"id,omitempty"`
Name string `json:"name,omitempty" structs:"name,omitempty"`
Links *SelfLink `json:"_links,omitempty" structs:"_links,omitempty"`
}
// OrganizationUsersDTO contains organization user ids
type OrganizationUsersDTO struct {
AccountIds []string `json:"accountIds,omitempty" structs:"accountIds,omitempty"`
}
// PagedDTO is response of a paged list
type PagedDTO struct {
Size int `json:"size,omitempty" structs:"size,omitempty"`
Start int `json:"start,omitempty" structs:"start,omitempty"`
Limit int `limit:"size,omitempty" structs:"limit,omitempty"`
IsLastPage bool `json:"isLastPage,omitempty" structs:"isLastPage,omitempty"`
Values []interface{} `values:"isLastPage,omitempty" structs:"values,omitempty"`
Expands []string `json:"_expands,omitempty" structs:"_expands,omitempty"`
}
// PropertyKey contains Property key details.
type PropertyKey struct {
Self string `json:"self,omitempty" structs:"self,omitempty"`
Key string `json:"key,omitempty" structs:"key,omitempty"`
}
// PropertyKeys contains an array of PropertyKey
type PropertyKeys struct {
Keys []PropertyKey `json:"keys,omitempty" structs:"keys,omitempty"`
}
// GetAllOrganizationsWithContext returns a list of organizations in
// the Jira Service Management instance.
// Use this method when you want to present a list
// of organizations or want to locate an organization
// by name.
//
// Jira API docs: https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-organization/#api-group-organization
func (s *OrganizationService) GetAllOrganizationsWithContext(ctx context.Context, start int, limit int, accountID string) (*PagedDTO, *Response, error) {
apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization?start=%d&limit=%d", start, limit)
if accountID != "" {
apiEndPoint += fmt.Sprintf("&accountId=%s", accountID)
}
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndPoint, nil)
req.Header.Set("Accept", "application/json")
if err != nil {
return nil, nil, err
}
v := new(PagedDTO)
resp, err := s.client.Do(req, v)
if err != nil {
jerr := NewJiraError(resp, err)
return nil, resp, jerr
}
return v, resp, nil
}
// GetAllOrganizations wraps GetAllOrganizationsWithContext using the background context.
func (s *OrganizationService) GetAllOrganizations(start int, limit int, accountID string) (*PagedDTO, *Response, error) {
return s.GetAllOrganizationsWithContext(context.Background(), start, limit, accountID)
}
// CreateOrganizationWithContext creates an organization by
// passing the name of the organization.
//
// Jira API docs: https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-organization/#api-rest-servicedeskapi-organization-post
func (s *OrganizationService) CreateOrganizationWithContext(ctx context.Context, name string) (*Organization, *Response, error) {
apiEndPoint := "rest/servicedeskapi/organization"
organization := OrganizationCreationDTO{
Name: name,
}
req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndPoint, organization)
req.Header.Set("Accept", "application/json")
if err != nil {
return nil, nil, err
}
o := new(Organization)
resp, err := s.client.Do(req, &o)
if err != nil {
jerr := NewJiraError(resp, err)
return nil, resp, jerr
}
return o, resp, nil
}
// CreateOrganization wraps CreateOrganizationWithContext using the background context.
func (s *OrganizationService) CreateOrganization(name string) (*Organization, *Response, error) {
return s.CreateOrganizationWithContext(context.Background(), name)
}
// GetOrganizationWithContext returns details of an
// organization. Use this method to get organization
// details whenever your application component is
// passed an organization ID but needs to display
// other organization details.
//
// Jira API docs: https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-organization/#api-rest-servicedeskapi-organization-organizationid-get
func (s *OrganizationService) GetOrganizationWithContext(ctx context.Context, organizationID int) (*Organization, *Response, error) {
apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d", organizationID)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndPoint, nil)
req.Header.Set("Accept", "application/json")
if err != nil {
return nil, nil, err
}
o := new(Organization)
resp, err := s.client.Do(req, &o)
if err != nil {
jerr := NewJiraError(resp, err)
return nil, resp, jerr
}
return o, resp, nil
}
// GetOrganization wraps GetOrganizationWithContext using the background context.
func (s *OrganizationService) GetOrganization(organizationID int) (*Organization, *Response, error) {
return s.GetOrganizationWithContext(context.Background(), organizationID)
}
// DeleteOrganizationWithContext deletes an organization. Note that
// the organization is deleted regardless
// of other associations it may have.
// For example, associations with service desks.
//
// Jira API docs: https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-organization/#api-rest-servicedeskapi-organization-organizationid-delete
// Caller must close resp.Body
func (s *OrganizationService) DeleteOrganizationWithContext(ctx context.Context, organizationID int) (*Response, error) {
apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d", organizationID)
req, err := s.client.NewRequestWithContext(ctx, "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
}
// DeleteOrganization wraps DeleteOrganizationWithContext using the background context.
// Caller must close resp.Body
func (s *OrganizationService) DeleteOrganization(organizationID int) (*Response, error) {
return s.DeleteOrganizationWithContext(context.Background(), organizationID)
}
// GetPropertiesKeysWithContext returns the keys of
// all properties for an organization. Use this resource
// when you need to find out what additional properties
// items have been added to an organization.
//
// https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-organization/#api-rest-servicedeskapi-organization-organizationid-property-get
func (s *OrganizationService) GetPropertiesKeysWithContext(ctx context.Context, organizationID int) (*PropertyKeys, *Response, error) {
apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d/property", organizationID)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndPoint, nil)
req.Header.Set("Accept", "application/json")
if err != nil {
return nil, nil, err
}
pk := new(PropertyKeys)
resp, err := s.client.Do(req, &pk)
if err != nil {
jerr := NewJiraError(resp, err)
return nil, resp, jerr
}
return pk, resp, nil
}
// GetPropertiesKeys wraps GetPropertiesKeysWithContext using the background context.
func (s *OrganizationService) GetPropertiesKeys(organizationID int) (*PropertyKeys, *Response, error) {
return s.GetPropertiesKeysWithContext(context.Background(), organizationID)
}
// GetPropertyWithContext returns the value of a property
// from an organization. Use this method to obtain the JSON
// content for an organization's property.
//
// https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-organization/#api-rest-servicedeskapi-organization-organizationid-property-propertykey-get
func (s *OrganizationService) GetPropertyWithContext(ctx context.Context, organizationID int, propertyKey string) (*EntityProperty, *Response, error) {
apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d/property/%s", organizationID, propertyKey)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndPoint, nil)
req.Header.Set("Accept", "application/json")
if err != nil {
return nil, nil, err
}
ep := new(EntityProperty)
resp, err := s.client.Do(req, &ep)
if err != nil {
jerr := NewJiraError(resp, err)
return nil, resp, jerr
}
return ep, resp, nil
}
// GetProperty wraps GetPropertyWithContext using the background context.
func (s *OrganizationService) GetProperty(organizationID int, propertyKey string) (*EntityProperty, *Response, error) {
return s.GetPropertyWithContext(context.Background(), organizationID, propertyKey)
}
// SetPropertyWithContext sets the value of a
// property for an organization. Use this
// resource to store custom data against an organization.
//
// https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-organization/#api-rest-servicedeskapi-organization-organizationid-property-propertykey-put
// Caller must close resp.Body
func (s *OrganizationService) SetPropertyWithContext(ctx context.Context, organizationID int, propertyKey string) (*Response, error) {
apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d/property/%s", organizationID, propertyKey)
req, err := s.client.NewRequestWithContext(ctx, "PUT", apiEndPoint, nil)
req.Header.Set("Accept", "application/json")
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
}
// SetProperty wraps SetPropertyWithContext using the background context.
// Caller must close resp.Body
func (s *OrganizationService) SetProperty(organizationID int, propertyKey string) (*Response, error) {
return s.SetPropertyWithContext(context.Background(), organizationID, propertyKey)
}
// DeletePropertyWithContext removes a property from an organization.
//
// https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-organization/#api-rest-servicedeskapi-organization-organizationid-property-propertykey-delete
// Caller must close resp.Body
func (s *OrganizationService) DeletePropertyWithContext(ctx context.Context, organizationID int, propertyKey string) (*Response, error) {
apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d/property/%s", organizationID, propertyKey)
req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndPoint, nil)
req.Header.Set("Accept", "application/json")
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
}
// DeleteProperty wraps DeletePropertyWithContext using the background context.
// Caller must close resp.Body
func (s *OrganizationService) DeleteProperty(organizationID int, propertyKey string) (*Response, error) {
return s.DeletePropertyWithContext(context.Background(), organizationID, propertyKey)
}
// GetUsersWithContext returns all the users
// associated with an organization. Use this
// method where you want to provide a list of
// users for an organization or determine if
// a user is associated with an organization.
//
// https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-organization/#api-rest-servicedeskapi-organization-organizationid-user-get
func (s *OrganizationService) GetUsersWithContext(ctx context.Context, organizationID int, start int, limit int) (*PagedDTO, *Response, error) {
apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d/user?start=%d&limit=%d", organizationID, start, limit)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndPoint, nil)
req.Header.Set("Accept", "application/json")
if err != nil {
return nil, nil, err
}
users := new(PagedDTO)
resp, err := s.client.Do(req, &users)
if err != nil {
jerr := NewJiraError(resp, err)
return nil, resp, jerr
}
return users, resp, nil
}
// GetUsers wraps GetUsersWithContext using the background context.
func (s *OrganizationService) GetUsers(organizationID int, start int, limit int) (*PagedDTO, *Response, error) {
return s.GetUsersWithContext(context.Background(), organizationID, start, limit)
}
// AddUsersWithContext adds users to an organization.
//
// https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-organization/#api-rest-servicedeskapi-organization-organizationid-user-post
// Caller must close resp.Body
func (s *OrganizationService) AddUsersWithContext(ctx context.Context, organizationID int, users OrganizationUsersDTO) (*Response, error) {
apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d/user", organizationID)
req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndPoint, users)
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
}
// AddUsers wraps AddUsersWithContext using the background context.
// Caller must close resp.Body
func (s *OrganizationService) AddUsers(organizationID int, users OrganizationUsersDTO) (*Response, error) {
return s.AddUsersWithContext(context.Background(), organizationID, users)
}
// RemoveUsersWithContext removes users from an organization.
//
// https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-organization/#api-rest-servicedeskapi-organization-organizationid-user-delete
// Caller must close resp.Body
func (s *OrganizationService) RemoveUsersWithContext(ctx context.Context, organizationID int, users OrganizationUsersDTO) (*Response, error) {
apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d/user", organizationID)
req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndPoint, nil)
req.Header.Set("Accept", "application/json")
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
}
// RemoveUsers wraps RemoveUsersWithContext using the background context.
// Caller must close resp.Body
func (s *OrganizationService) RemoveUsers(organizationID int, users OrganizationUsersDTO) (*Response, error) {
return s.RemoveUsersWithContext(context.Background(), organizationID, users)
}

View file

@ -1,10 +1,13 @@
package jira
import "fmt"
import (
"context"
"fmt"
)
// PermissionSchemeService handles permissionschemes for the JIRA instance / API.
// PermissionSchemeService handles permissionschemes for the Jira instance / API.
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-group-Permissionscheme
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-group-Permissionscheme
type PermissionSchemeService struct {
client *Client
}
@ -25,12 +28,12 @@ type Holder struct {
Expand string `json:"expand" structs:"expand"`
}
// GetList returns a list of all permission schemes
// GetListWithContext 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) {
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-permissionscheme-get
func (s *PermissionSchemeService) GetListWithContext(ctx context.Context) (*PermissionSchemes, *Response, error) {
apiEndpoint := "/rest/api/3/permissionscheme"
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
@ -45,12 +48,17 @@ func (s *PermissionSchemeService) GetList() (*PermissionSchemes, *Response, erro
return pss, resp, nil
}
// Get returns a full representation of the permission scheme for the schemeID
// GetList wraps GetListWithContext using the background context.
func (s *PermissionSchemeService) GetList() (*PermissionSchemes, *Response, error) {
return s.GetListWithContext(context.Background())
}
// GetWithContext 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) {
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-permissionscheme-schemeId-get
func (s *PermissionSchemeService) GetWithContext(ctx context.Context, schemeID int) (*PermissionScheme, *Response, error) {
apiEndpoint := fmt.Sprintf("/rest/api/3/permissionscheme/%d", schemeID)
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
@ -62,8 +70,13 @@ func (s *PermissionSchemeService) Get(schemeID int) (*PermissionScheme, *Respons
return nil, resp, jerr
}
if ps.Self == "" {
return nil, resp, fmt.Errorf("No permissionscheme with ID %d found", schemeID)
return nil, resp, fmt.Errorf("no permissionscheme with ID %d found", schemeID)
}
return ps, resp, nil
}
// Get wraps GetWithContext using the background context.
func (s *PermissionSchemeService) Get(schemeID int) (*PermissionScheme, *Response, error) {
return s.GetWithContext(context.Background(), schemeID)
}

View file

@ -1,13 +1,15 @@
package jira
// PriorityService handles priorities for the JIRA instance / API.
import "context"
// PriorityService handles priorities for the Jira instance / API.
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-Priority
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-Priority
type PriorityService struct {
client *Client
}
// Priority represents a priority of a JIRA issue.
// Priority represents a priority of a Jira issue.
// Typical types are "Normal", "Moderate", "Urgent", ...
type Priority struct {
Self string `json:"self,omitempty" structs:"self,omitempty"`
@ -18,12 +20,12 @@ type Priority struct {
Description string `json:"description,omitempty" structs:"description,omitempty"`
}
// GetList gets all priorities from JIRA
// GetListWithContext gets all priorities from Jira
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-priority-get
func (s *PriorityService) GetList() ([]Priority, *Response, error) {
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-priority-get
func (s *PriorityService) GetListWithContext(ctx context.Context) ([]Priority, *Response, error) {
apiEndpoint := "rest/api/2/priority"
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
@ -35,3 +37,8 @@ func (s *PriorityService) GetList() ([]Priority, *Response, error) {
}
return priorityList, resp, nil
}
// GetList wraps GetListWithContext using the background context.
func (s *PriorityService) GetList() ([]Priority, *Response, error) {
return s.GetListWithContext(context.Background())
}

View file

@ -1,14 +1,15 @@
package jira
import (
"context"
"fmt"
"github.com/google/go-querystring/query"
)
// ProjectService handles projects for the JIRA instance / API.
// ProjectService handles projects for the Jira instance / API.
//
// JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/project
// Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/project
type ProjectService struct {
client *Client
}
@ -34,7 +35,7 @@ type ProjectCategory struct {
Description string `json:"description" structs:"description,omitempty"`
}
// Project represents a JIRA Project.
// Project represents a Jira Project.
type Project struct {
Expand string `json:"expand,omitempty" structs:"expand,omitempty"`
Self string `json:"self,omitempty" structs:"self,omitempty"`
@ -80,20 +81,25 @@ type PermissionScheme struct {
Permissions []Permission `json:"permissions" structs:"permissions,omitempty"`
}
// GetList gets all projects form JIRA
// GetListWithContext gets all projects form Jira
//
// JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/project-getAllProjects
func (s *ProjectService) GetList() (*ProjectList, *Response, error) {
return s.ListWithOptions(&GetQueryOptions{})
// Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/project-getAllProjects
func (s *ProjectService) GetListWithContext(ctx context.Context) (*ProjectList, *Response, error) {
return s.ListWithOptionsWithContext(ctx, &GetQueryOptions{})
}
// ListWithOptions gets all projects form JIRA with optional query params, like &GetQueryOptions{Expand: "issueTypes"} to get
// GetList wraps GetListWithContext using the background context.
func (s *ProjectService) GetList() (*ProjectList, *Response, error) {
return s.GetListWithContext(context.Background())
}
// ListWithOptionsWithContext gets all projects form Jira with optional query params, like &GetQueryOptions{Expand: "issueTypes"} to get
// a list of all projects and their supported issuetypes
//
// JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/project-getAllProjects
func (s *ProjectService) ListWithOptions(options *GetQueryOptions) (*ProjectList, *Response, error) {
// Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/project-getAllProjects
func (s *ProjectService) ListWithOptionsWithContext(ctx context.Context, options *GetQueryOptions) (*ProjectList, *Response, error) {
apiEndpoint := "rest/api/2/project"
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
@ -116,14 +122,19 @@ func (s *ProjectService) ListWithOptions(options *GetQueryOptions) (*ProjectList
return projectList, resp, nil
}
// Get returns a full representation of the project for the given issue key.
// JIRA will attempt to identify the project by the projectIdOrKey path parameter.
// ListWithOptions wraps ListWithOptionsWithContext using the background context.
func (s *ProjectService) ListWithOptions(options *GetQueryOptions) (*ProjectList, *Response, error) {
return s.ListWithOptionsWithContext(context.Background(), options)
}
// GetWithContext returns a full representation of the project for the given issue key.
// Jira will attempt to identify the project by the projectIdOrKey path parameter.
// This can be an project id, or an project key.
//
// JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/project-getProject
func (s *ProjectService) Get(projectID string) (*Project, *Response, error) {
// Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/project-getProject
func (s *ProjectService) GetWithContext(ctx context.Context, projectID string) (*Project, *Response, error) {
apiEndpoint := fmt.Sprintf("rest/api/2/project/%s", projectID)
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
@ -138,14 +149,19 @@ func (s *ProjectService) Get(projectID string) (*Project, *Response, error) {
return project, resp, nil
}
// GetPermissionScheme returns a full representation of the permission scheme for the project
// JIRA will attempt to identify the project by the projectIdOrKey path parameter.
// Get wraps GetWithContext using the background context.
func (s *ProjectService) Get(projectID string) (*Project, *Response, error) {
return s.GetWithContext(context.Background(), projectID)
}
// GetPermissionSchemeWithContext returns a full representation of the permission scheme for the project
// Jira will attempt to identify the project by the projectIdOrKey path parameter.
// This can be an project id, or an project key.
//
// JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/project-getProject
func (s *ProjectService) GetPermissionScheme(projectID string) (*PermissionScheme, *Response, error) {
// Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/project-getProject
func (s *ProjectService) GetPermissionSchemeWithContext(ctx context.Context, projectID string) (*PermissionScheme, *Response, error) {
apiEndpoint := fmt.Sprintf("/rest/api/2/project/%s/permissionscheme", projectID)
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
@ -159,3 +175,8 @@ func (s *ProjectService) GetPermissionScheme(projectID string) (*PermissionSchem
return ps, resp, nil
}
// GetPermissionScheme wraps GetPermissionSchemeWithContext using the background context.
func (s *ProjectService) GetPermissionScheme(projectID string) (*PermissionScheme, *Response, error) {
return s.GetPermissionSchemeWithContext(context.Background(), projectID)
}

123
vendor/github.com/andygrunwald/go-jira/request.go generated vendored Normal file
View file

@ -0,0 +1,123 @@
package jira
import (
"context"
"fmt"
)
// RequestService handles ServiceDesk customer requests for the Jira instance / API.
type RequestService struct {
client *Client
}
// Request represents a ServiceDesk customer request.
type Request struct {
IssueID string `json:"issueId,omitempty" structs:"issueId,omitempty"`
IssueKey string `json:"issueKey,omitempty" structs:"issueKey,omitempty"`
TypeID string `json:"requestTypeId,omitempty" structs:"requestTypeId,omitempty"`
ServiceDeskID string `json:"serviceDeskId,omitempty" structs:"serviceDeskId,omitempty"`
Reporter *Customer `json:"reporter,omitempty" structs:"reporter,omitempty"`
FieldValues []RequestFieldValue `json:"requestFieldValues,omitempty" structs:"requestFieldValues,omitempty"`
Status *RequestStatus `json:"currentStatus,omitempty" structs:"currentStatus,omitempty"`
Links *SelfLink `json:"_links,omitempty" structs:"_links,omitempty"`
Expands []string `json:"_expands,omitempty" structs:"_expands,omitempty"`
}
// RequestFieldValue is a request field.
type RequestFieldValue struct {
FieldID string `json:"fieldId,omitempty" structs:"fieldId,omitempty"`
Label string `json:"label,omitempty" structs:"label,omitempty"`
Value string `json:"value,omitempty" structs:"value,omitempty"`
}
// RequestDate is the date format used in requests.
type RequestDate struct {
ISO8601 string `json:"iso8601,omitempty" structs:"iso8601,omitempty"`
Jira string `json:"jira,omitempty" structs:"jira,omitempty"`
Friendly string `json:"friendly,omitempty" structs:"friendly,omitempty"`
Epoch int64 `json:"epoch,omitempty" structs:"epoch,omitempty"`
}
// RequestStatus is the status for a request.
type RequestStatus struct {
Status string
Category string
Date RequestDate
}
// RequestComment is a comment for a request.
type RequestComment struct {
ID string `json:"id,omitempty" structs:"id,omitempty"`
Body string `json:"body,omitempty" structs:"body,omitempty"`
Public bool `json:"public" structs:"public"`
Author *Customer `json:"author,omitempty" structs:"author,omitempty"`
Created *RequestDate `json:"created,omitempty" structs:"created,omitempty"`
Links *SelfLink `json:"_links,omitempty" structs:"_links,omitempty"`
Expands []string `json:"_expands,omitempty" structs:"_expands,omitempty"`
}
// CreateWithContext creates a new request.
//
// https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-request/#api-rest-servicedeskapi-request-post
func (r *RequestService) CreateWithContext(ctx context.Context, requester string, participants []string, request *Request) (*Request, *Response, error) {
apiEndpoint := "rest/servicedeskapi/request"
payload := struct {
*Request
FieldValues map[string]string `json:"requestFieldValues,omitempty"`
Requester string `json:"raiseOnBehalfOf,omitempty"`
Participants []string `json:"requestParticipants,omitempty"`
}{
Request: request,
FieldValues: make(map[string]string),
Requester: requester,
Participants: participants,
}
for _, field := range request.FieldValues {
payload.FieldValues[field.FieldID] = field.Value
}
req, err := r.client.NewRequestWithContext(ctx, "POST", apiEndpoint, payload)
if err != nil {
return nil, nil, err
}
responseRequest := new(Request)
resp, err := r.client.Do(req, responseRequest)
if err != nil {
return nil, resp, NewJiraError(resp, err)
}
return responseRequest, resp, nil
}
// Create wraps CreateWithContext using the background context.
func (r *RequestService) Create(requester string, participants []string, request *Request) (*Request, *Response, error) {
return r.CreateWithContext(context.Background(), requester, participants, request)
}
// CreateCommentWithContext creates a comment on a request.
//
// https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-request/#api-rest-servicedeskapi-request-issueidorkey-comment-post
func (r *RequestService) CreateCommentWithContext(ctx context.Context, issueIDOrKey string, comment *RequestComment) (*RequestComment, *Response, error) {
apiEndpoint := fmt.Sprintf("rest/servicedeskapi/request/%v/comment", issueIDOrKey)
req, err := r.client.NewRequestWithContext(ctx, "POST", apiEndpoint, comment)
if err != nil {
return nil, nil, err
}
responseComment := new(RequestComment)
resp, err := r.client.Do(req, responseComment)
if err != nil {
return nil, resp, NewJiraError(resp, err)
}
return responseComment, resp, nil
}
// CreateComment wraps CreateCommentWithContext using the background context.
func (r *RequestService) CreateComment(issueIDOrKey string, comment *RequestComment) (*RequestComment, *Response, error) {
return r.CreateCommentWithContext(context.Background(), issueIDOrKey, comment)
}

View file

@ -0,0 +1,24 @@
//go:build go1.13
// +build go1.13
// This file provides glue to use Context in `http.Request` with
// Go version 1.13 and higher.
// The function `http.NewRequestWithContext` has been added in Go 1.13.
// Before the release 1.13, to use Context we need creat `http.Request`
// then use the method `WithContext` to create a new `http.Request`
// with Context from the existing `http.Request`.
//
// Doc: https://golang.org/doc/go1.13#net/http
package jira
import (
"context"
"io"
"net/http"
)
func newRequestWithContext(ctx context.Context, method, url string, body io.Reader) (*http.Request, error) {
return http.NewRequestWithContext(ctx, method, url, body)
}

View file

@ -0,0 +1,29 @@
//go:build !go1.13
// +build !go1.13
// This file provides glue to use Context in `http.Request` with
// Go version before 1.13.
// The function `http.NewRequestWithContext` has been added in Go 1.13.
// Before the release 1.13, to use Context we need creat `http.Request`
// then use the method `WithContext` to create a new `http.Request`
// with Context from the existing `http.Request`.
//
// Doc: https://golang.org/doc/go1.13#net/http
package jira
import (
"context"
"io"
"net/http"
)
func newRequestWithContext(ctx context.Context, method, url string, body io.Reader) (*http.Request, error) {
r, err := http.NewRequest(method, url, body)
if err != nil {
return nil, err
}
return r.WithContext(ctx), nil
}

View file

@ -1,13 +1,15 @@
package jira
// ResolutionService handles resolutions for the JIRA instance / API.
import "context"
// ResolutionService handles resolutions for the Jira instance / API.
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-Resolution
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-Resolution
type ResolutionService struct {
client *Client
}
// Resolution represents a resolution of a JIRA issue.
// Resolution represents a resolution of a Jira issue.
// Typical types are "Fixed", "Suspended", "Won't Fix", ...
type Resolution struct {
Self string `json:"self" structs:"self"`
@ -16,12 +18,12 @@ type Resolution struct {
Name string `json:"name" structs:"name"`
}
// GetList gets all resolutions from JIRA
// GetListWithContext gets all resolutions from Jira
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-resolution-get
func (s *ResolutionService) GetList() ([]Resolution, *Response, error) {
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-resolution-get
func (s *ResolutionService) GetListWithContext(ctx context.Context) ([]Resolution, *Response, error) {
apiEndpoint := "rest/api/2/resolution"
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
@ -33,3 +35,8 @@ func (s *ResolutionService) GetList() ([]Resolution, *Response, error) {
}
return resolutionList, resp, nil
}
// GetList wraps GetListWithContext using the background context.
func (s *ResolutionService) GetList() ([]Resolution, *Response, error) {
return s.GetListWithContext(context.Background())
}

View file

@ -1,17 +1,18 @@
package jira
import (
"context"
"fmt"
)
// RoleService handles roles for the JIRA instance / API.
// RoleService handles roles for the Jira instance / API.
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-group-Role
// 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
// Role represents a Jira product role
type Role struct {
Self string `json:"self" structs:"self"`
Name string `json:"name" structs:"name"`
@ -20,7 +21,7 @@ type Role struct {
Actors []*Actor `json:"actors" structs:"actors"`
}
// Actor represents a JIRA actor
// Actor represents a Jira actor
type Actor struct {
ID int `json:"id" structs:"id"`
DisplayName string `json:"displayName" structs:"displayName"`
@ -35,12 +36,12 @@ type ActorUser struct {
AccountID string `json:"accountId" structs:"accountId"`
}
// GetList returns a list of all available project roles
// GetListWithContext 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) {
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-role-get
func (s *RoleService) GetListWithContext(ctx context.Context) (*[]Role, *Response, error) {
apiEndpoint := "rest/api/3/role"
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
@ -53,12 +54,17 @@ func (s *RoleService) GetList() (*[]Role, *Response, error) {
return roles, resp, err
}
// Get retreives a single Role from Jira
// GetList wraps GetListWithContext using the background context.
func (s *RoleService) GetList() (*[]Role, *Response, error) {
return s.GetListWithContext(context.Background())
}
// GetWithContext 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) {
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-role-id-get
func (s *RoleService) GetWithContext(ctx context.Context, roleID int) (*Role, *Response, error) {
apiEndpoint := fmt.Sprintf("rest/api/3/role/%d", roleID)
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
@ -69,8 +75,13 @@ func (s *RoleService) Get(roleID int) (*Role, *Response, error) {
return nil, resp, jerr
}
if role.Self == "" {
return nil, resp, fmt.Errorf("No role with ID %d found", roleID)
return nil, resp, fmt.Errorf("no role with ID %d found", roleID)
}
return role, resp, err
}
// Get wraps GetWithContext using the background context.
func (s *RoleService) Get(roleID int) (*Role, *Response, error) {
return s.GetWithContext(context.Background(), roleID)
}

227
vendor/github.com/andygrunwald/go-jira/servicedesk.go generated vendored Normal file
View file

@ -0,0 +1,227 @@
package jira
import (
"context"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"github.com/google/go-querystring/query"
)
// ServiceDeskService handles ServiceDesk for the Jira instance / API.
type ServiceDeskService struct {
client *Client
}
// ServiceDeskOrganizationDTO is a DTO for ServiceDesk organizations
type ServiceDeskOrganizationDTO struct {
OrganizationID int `json:"organizationId,omitempty" structs:"organizationId,omitempty"`
}
// GetOrganizationsWithContext returns a list of
// all organizations associated with a service desk.
//
// https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-organization/#api-rest-servicedeskapi-servicedesk-servicedeskid-organization-get
func (s *ServiceDeskService) GetOrganizationsWithContext(ctx context.Context, serviceDeskID interface{}, start int, limit int, accountID string) (*PagedDTO, *Response, error) {
apiEndPoint := fmt.Sprintf("rest/servicedeskapi/servicedesk/%v/organization?start=%d&limit=%d", serviceDeskID, start, limit)
if accountID != "" {
apiEndPoint += fmt.Sprintf("&accountId=%s", accountID)
}
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndPoint, nil)
req.Header.Set("Accept", "application/json")
if err != nil {
return nil, nil, err
}
orgs := new(PagedDTO)
resp, err := s.client.Do(req, &orgs)
if err != nil {
jerr := NewJiraError(resp, err)
return nil, resp, jerr
}
return orgs, resp, nil
}
// GetOrganizations wraps GetOrganizationsWithContext using the background context.
func (s *ServiceDeskService) GetOrganizations(serviceDeskID interface{}, start int, limit int, accountID string) (*PagedDTO, *Response, error) {
return s.GetOrganizationsWithContext(context.Background(), serviceDeskID, start, limit, accountID)
}
// AddOrganizationWithContext adds an organization to
// a service desk. If the organization ID is already
// associated with the service desk, no change is made
// and the resource returns a 204 success code.
//
// https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-organization/#api-rest-servicedeskapi-servicedesk-servicedeskid-organization-post
// Caller must close resp.Body
func (s *ServiceDeskService) AddOrganizationWithContext(ctx context.Context, serviceDeskID interface{}, organizationID int) (*Response, error) {
apiEndPoint := fmt.Sprintf("rest/servicedeskapi/servicedesk/%v/organization", serviceDeskID)
organization := ServiceDeskOrganizationDTO{
OrganizationID: organizationID,
}
req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndPoint, organization)
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
}
// AddOrganization wraps AddOrganizationWithContext using the background context.
// Caller must close resp.Body
func (s *ServiceDeskService) AddOrganization(serviceDeskID interface{}, organizationID int) (*Response, error) {
return s.AddOrganizationWithContext(context.Background(), serviceDeskID, organizationID)
}
// RemoveOrganizationWithContext removes an organization
// from a service desk. If the organization ID does not
// match an organization associated with the service desk,
// no change is made and the resource returns a 204 success code.
//
// https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-organization/#api-rest-servicedeskapi-servicedesk-servicedeskid-organization-delete
// Caller must close resp.Body
func (s *ServiceDeskService) RemoveOrganizationWithContext(ctx context.Context, serviceDeskID interface{}, organizationID int) (*Response, error) {
apiEndPoint := fmt.Sprintf("rest/servicedeskapi/servicedesk/%v/organization", serviceDeskID)
organization := ServiceDeskOrganizationDTO{
OrganizationID: organizationID,
}
req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndPoint, organization)
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
}
// RemoveOrganization wraps RemoveOrganizationWithContext using the background context.
// Caller must close resp.Body
func (s *ServiceDeskService) RemoveOrganization(serviceDeskID interface{}, organizationID int) (*Response, error) {
return s.RemoveOrganizationWithContext(context.Background(), serviceDeskID, organizationID)
}
// AddCustomersWithContext adds customers to the given service desk.
//
// https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-servicedesk/#api-rest-servicedeskapi-servicedesk-servicedeskid-customer-post
func (s *ServiceDeskService) AddCustomersWithContext(ctx context.Context, serviceDeskID interface{}, acountIDs ...string) (*Response, error) {
apiEndpoint := fmt.Sprintf("rest/servicedeskapi/servicedesk/%v/customer", serviceDeskID)
payload := struct {
AccountIDs []string `json:"accountIds"`
}{
AccountIDs: acountIDs,
}
req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, payload)
if err != nil {
return nil, err
}
resp, err := s.client.Do(req, nil)
if err != nil {
return resp, NewJiraError(resp, err)
}
defer resp.Body.Close()
_, _ = io.Copy(ioutil.Discard, resp.Body)
return resp, nil
}
// AddCustomers wraps AddCustomersWithContext using the background context.
func (s *ServiceDeskService) AddCustomers(serviceDeskID interface{}, acountIDs ...string) (*Response, error) {
return s.AddCustomersWithContext(context.Background(), serviceDeskID, acountIDs...)
}
// RemoveCustomersWithContext removes customers to the given service desk.
//
// https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-servicedesk/#api-rest-servicedeskapi-servicedesk-servicedeskid-customer-delete
func (s *ServiceDeskService) RemoveCustomersWithContext(ctx context.Context, serviceDeskID interface{}, acountIDs ...string) (*Response, error) {
apiEndpoint := fmt.Sprintf("rest/servicedeskapi/servicedesk/%v/customer", serviceDeskID)
payload := struct {
AccountIDs []string `json:"accountIDs"`
}{
AccountIDs: acountIDs,
}
req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, payload)
if err != nil {
return nil, err
}
resp, err := s.client.Do(req, nil)
if err != nil {
return resp, NewJiraError(resp, err)
}
defer resp.Body.Close()
_, _ = io.Copy(ioutil.Discard, resp.Body)
return resp, nil
}
// RemoveCustomers wraps RemoveCustomersWithContext using the background context.
func (s *ServiceDeskService) RemoveCustomers(serviceDeskID interface{}, acountIDs ...string) (*Response, error) {
return s.RemoveCustomersWithContext(context.Background(), serviceDeskID, acountIDs...)
}
// ListCustomersWithContext lists customers for a ServiceDesk.
//
// https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-servicedesk/#api-rest-servicedeskapi-servicedesk-servicedeskid-customer-get
func (s *ServiceDeskService) ListCustomersWithContext(ctx context.Context, serviceDeskID interface{}, options *CustomerListOptions) (*CustomerList, *Response, error) {
apiEndpoint := fmt.Sprintf("rest/servicedeskapi/servicedesk/%v/customer", serviceDeskID)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
// this is an experiemntal endpoint
req.Header.Set("X-ExperimentalApi", "opt-in")
if options != nil {
q, err := query.Values(options)
if err != nil {
return nil, nil, err
}
req.URL.RawQuery = q.Encode()
}
resp, err := s.client.Do(req, nil)
if err != nil {
return nil, resp, NewJiraError(resp, err)
}
defer resp.Body.Close()
customerList := new(CustomerList)
if err := json.NewDecoder(resp.Body).Decode(customerList); err != nil {
return nil, resp, fmt.Errorf("could not unmarshall the data into struct")
}
return customerList, resp, nil
}
// ListCustomers wraps ListCustomersWithContext using the background context.
func (s *ServiceDeskService) ListCustomers(serviceDeskID interface{}, options *CustomerListOptions) (*CustomerList, *Response, error) {
return s.ListCustomersWithContext(context.Background(), serviceDeskID, options)
}

View file

@ -1,12 +1,13 @@
package jira
import (
"context"
"fmt"
"github.com/google/go-querystring/query"
)
// SprintService handles sprints in JIRA Agile API.
// SprintService handles sprints in Jira Agile API.
// See https://docs.atlassian.com/jira-software/REST/cloud/
type SprintService struct {
client *Client
@ -22,17 +23,18 @@ type IssuesInSprintResult struct {
Issues []Issue `json:"issues"`
}
// MoveIssuesToSprint moves issues to a sprint, for a given sprint Id.
// MoveIssuesToSprintWithContext moves issues to a sprint, for a given sprint Id.
// Issues can only be moved to open or active sprints.
// The maximum number of issues that can be moved in one operation is 50.
//
// JIRA API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/sprint-moveIssuesToSprint
func (s *SprintService) MoveIssuesToSprint(sprintID int, issueIDs []string) (*Response, error) {
// Jira API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/sprint-moveIssuesToSprint
// Caller must close resp.Body
func (s *SprintService) MoveIssuesToSprintWithContext(ctx context.Context, sprintID int, issueIDs []string) (*Response, error) {
apiEndpoint := fmt.Sprintf("rest/agile/1.0/sprint/%d/issue", sprintID)
payload := IssuesWrapper{Issues: issueIDs}
req, err := s.client.NewRequest("POST", apiEndpoint, payload)
req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, payload)
if err != nil {
return nil, err
@ -45,15 +47,21 @@ func (s *SprintService) MoveIssuesToSprint(sprintID int, issueIDs []string) (*Re
return resp, err
}
// GetIssuesForSprint returns all issues in a sprint, for a given sprint Id.
// MoveIssuesToSprint wraps MoveIssuesToSprintWithContext using the background context.
// Caller must close resp.Body
func (s *SprintService) MoveIssuesToSprint(sprintID int, issueIDs []string) (*Response, error) {
return s.MoveIssuesToSprintWithContext(context.Background(), sprintID, issueIDs)
}
// GetIssuesForSprintWithContext returns all issues in a sprint, for a given sprint Id.
// This only includes issues that the user has permission to view.
// By default, the returned issues are ordered by rank.
//
// JIRA API Docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/sprint-getIssuesForSprint
func (s *SprintService) GetIssuesForSprint(sprintID int) ([]Issue, *Response, error) {
// Jira API Docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/sprint-getIssuesForSprint
func (s *SprintService) GetIssuesForSprintWithContext(ctx context.Context, sprintID int) ([]Issue, *Response, error) {
apiEndpoint := fmt.Sprintf("rest/agile/1.0/sprint/%d/issue", sprintID)
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
@ -68,20 +76,25 @@ func (s *SprintService) GetIssuesForSprint(sprintID int) ([]Issue, *Response, er
return result.Issues, resp, err
}
// GetIssue returns a full representation of the issue for the given issue key.
// JIRA will attempt to identify the issue by the issueIdOrKey path parameter.
// GetIssuesForSprint wraps GetIssuesForSprintWithContext using the background context.
func (s *SprintService) GetIssuesForSprint(sprintID int) ([]Issue, *Response, error) {
return s.GetIssuesForSprintWithContext(context.Background(), sprintID)
}
// GetIssueWithContext returns a full representation of the issue for the given issue key.
// Jira will attempt to identify the issue by the issueIdOrKey path parameter.
// This can be an issue id, or an issue key.
// If the issue cannot be found via an exact match, JIRA will also look for the issue in a case-insensitive way, or by looking to see if the issue was moved.
// If the issue cannot be found via an exact match, Jira will also look for the issue in a case-insensitive way, or by looking to see if the issue was moved.
//
// The given options will be appended to the query string
//
// JIRA API docs: https://docs.atlassian.com/jira-software/REST/7.3.1/#agile/1.0/issue-getIssue
// Jira API docs: https://docs.atlassian.com/jira-software/REST/7.3.1/#agile/1.0/issue-getIssue
//
// TODO: create agile service for holding all agile apis' implementation
func (s *SprintService) GetIssue(issueID string, options *GetQueryOptions) (*Issue, *Response, error) {
func (s *SprintService) GetIssueWithContext(ctx context.Context, issueID string, options *GetQueryOptions) (*Issue, *Response, error) {
apiEndpoint := fmt.Sprintf("rest/agile/1.0/issue/%s", issueID)
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
@ -105,3 +118,8 @@ func (s *SprintService) GetIssue(issueID string, options *GetQueryOptions) (*Iss
return issue, resp, nil
}
// GetIssue wraps GetIssueWithContext using the background context.
func (s *SprintService) GetIssue(issueID string, options *GetQueryOptions) (*Issue, *Response, error) {
return s.GetIssueWithContext(context.Background(), issueID, options)
}

View file

@ -1,15 +1,17 @@
package jira
// StatusService handles staties for the JIRA instance / API.
import "context"
// StatusService handles staties for the Jira instance / API.
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-group-Workflow-statuses
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-group-Workflow-statuses
type StatusService struct {
client *Client
}
// Status represents the current status of a JIRA issue.
// Status represents the current status of a Jira issue.
// Typical status are "Open", "In Progress", "Closed", ...
// Status can be user defined in every JIRA instance.
// Status can be user defined in every Jira instance.
type Status struct {
Self string `json:"self" structs:"self"`
Description string `json:"description" structs:"description"`
@ -19,12 +21,12 @@ type Status struct {
StatusCategory StatusCategory `json:"statusCategory" structs:"statusCategory"`
}
// GetAllStatuses returns a list of all statuses associated with workflows.
// GetAllStatusesWithContext returns a list of all statuses associated with workflows.
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-status-get
func (s *StatusService) GetAllStatuses() ([]Status, *Response, error) {
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-status-get
func (s *StatusService) GetAllStatusesWithContext(ctx context.Context) ([]Status, *Response, error) {
apiEndpoint := "rest/api/2/status"
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
@ -38,3 +40,8 @@ func (s *StatusService) GetAllStatuses() ([]Status, *Response, error) {
return statusList, resp, nil
}
// GetAllStatuses wraps GetAllStatusesWithContext using the background context.
func (s *StatusService) GetAllStatuses() ([]Status, *Response, error) {
return s.GetAllStatusesWithContext(context.Background())
}

View file

@ -1,14 +1,16 @@
package jira
// StatusCategoryService handles status categories for the JIRA instance / API.
import "context"
// StatusCategoryService handles status categories for the Jira instance / API.
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-Statuscategory
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-Statuscategory
type StatusCategoryService struct {
client *Client
}
// StatusCategory represents the category a status belongs to.
// Those categories can be user defined in every JIRA instance.
// Those categories can be user defined in every Jira instance.
type StatusCategory struct {
Self string `json:"self" structs:"self"`
ID int `json:"id" structs:"id"`
@ -17,7 +19,7 @@ type StatusCategory struct {
ColorName string `json:"colorName" structs:"colorName"`
}
// These constants are the keys of the default JIRA status categories
// These constants are the keys of the default Jira status categories
const (
StatusCategoryComplete = "done"
StatusCategoryInProgress = "indeterminate"
@ -25,12 +27,12 @@ const (
StatusCategoryUndefined = "undefined"
)
// GetList gets all status categories from JIRA
// GetListWithContext gets all status categories from Jira
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-statuscategory-get
func (s *StatusCategoryService) GetList() ([]StatusCategory, *Response, error) {
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-statuscategory-get
func (s *StatusCategoryService) GetListWithContext(ctx context.Context) ([]StatusCategory, *Response, error) {
apiEndpoint := "rest/api/2/statuscategory"
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
@ -42,3 +44,8 @@ func (s *StatusCategoryService) GetList() ([]StatusCategory, *Response, error) {
}
return statusCategoryList, resp, nil
}
// GetList wraps GetListWithContext using the background context.
func (s *StatusCategoryService) GetList() ([]StatusCategory, *Response, error) {
return s.GetListWithContext(context.Background())
}

9
vendor/github.com/andygrunwald/go-jira/types.go generated vendored Normal file
View file

@ -0,0 +1,9 @@
package jira
// Bool is a helper routine that allocates a new bool value
// to store v and returns a pointer to it.
func Bool(v bool) *bool {
p := new(bool)
*p = v
return p
}

View file

@ -1,25 +1,24 @@
package jira
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
)
// UserService handles users for the JIRA instance / API.
// UserService handles users for the Jira instance / API.
//
// JIRA API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/user
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-group-Users
type UserService struct {
client *Client
}
// User represents a JIRA user.
// User represents a Jira user.
type User struct {
Self string `json:"self,omitempty" structs:"self,omitempty"`
AccountID string `json:"accountId,omitempty" structs:"accountId,omitempty"`
AccountType string `json:"accountType,omitempty" structs:"accountType,omitempty"`
// TODO: name & key are deprecated, see:
// https://developer.atlassian.com/cloud/jira/platform/api-changes-for-user-privacy-announcement/
Self string `json:"self,omitempty" structs:"self,omitempty"`
AccountID string `json:"accountId,omitempty" structs:"accountId,omitempty"`
AccountType string `json:"accountType,omitempty" structs:"accountType,omitempty"`
Name string `json:"name,omitempty" structs:"name,omitempty"`
Key string `json:"key,omitempty" structs:"key,omitempty"`
Password string `json:"-"`
@ -47,12 +46,12 @@ type userSearch []userSearchParam
type userSearchF func(userSearch) userSearch
// Get gets user info from JIRA
// GetWithContext gets user info from Jira using its Account Id
//
// JIRA API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/user-getUser
func (s *UserService) Get(username string) (*User, *Response, error) {
apiEndpoint := fmt.Sprintf("/rest/api/2/user?username=%s", username)
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-user-get
func (s *UserService) GetWithContext(ctx context.Context, accountId string) (*User, *Response, error) {
apiEndpoint := fmt.Sprintf("/rest/api/2/user?accountId=%s", accountId)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
@ -65,12 +64,41 @@ func (s *UserService) Get(username string) (*User, *Response, error) {
return user, resp, nil
}
// Create creates an user in JIRA.
// Get wraps GetWithContext using the background context.
func (s *UserService) Get(accountId string) (*User, *Response, error) {
return s.GetWithContext(context.Background(), accountId)
}
// GetByAccountIDWithContext gets user info from Jira
// Searching by another parameter that is not accountId is deprecated,
// but this method is kept for backwards compatibility
// Jira API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/user-getUser
func (s *UserService) GetByAccountIDWithContext(ctx context.Context, accountID string) (*User, *Response, error) {
apiEndpoint := fmt.Sprintf("/rest/api/2/user?accountId=%s", accountID)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
user := new(User)
resp, err := s.client.Do(req, user)
if err != nil {
return nil, resp, NewJiraError(resp, err)
}
return user, resp, nil
}
// GetByAccountID wraps GetByAccountIDWithContext using the background context.
func (s *UserService) GetByAccountID(accountID string) (*User, *Response, error) {
return s.GetByAccountIDWithContext(context.Background(), accountID)
}
// CreateWithContext creates an user in Jira.
//
// JIRA API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/user-createUser
func (s *UserService) Create(user *User) (*User, *Response, error) {
// Jira API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/user-createUser
func (s *UserService) CreateWithContext(ctx context.Context, user *User) (*User, *Response, error) {
apiEndpoint := "/rest/api/2/user"
req, err := s.client.NewRequest("POST", apiEndpoint, user)
req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, user)
if err != nil {
return nil, nil, err
}
@ -84,24 +112,30 @@ func (s *UserService) Create(user *User) (*User, *Response, error) {
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
e := fmt.Errorf("Could not read the returned data")
e := fmt.Errorf("could not read the returned data")
return nil, resp, NewJiraError(resp, e)
}
err = json.Unmarshal(data, responseUser)
if err != nil {
e := fmt.Errorf("Could not unmarshall the data into struct")
e := fmt.Errorf("could not unmarshall the data into struct")
return nil, resp, NewJiraError(resp, e)
}
return responseUser, resp, nil
}
// Delete deletes an user from JIRA.
// Create wraps CreateWithContext using the background context.
func (s *UserService) Create(user *User) (*User, *Response, error) {
return s.CreateWithContext(context.Background(), user)
}
// DeleteWithContext 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)
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-user-delete
// Caller must close resp.Body
func (s *UserService) DeleteWithContext(ctx context.Context, accountId string) (*Response, error) {
apiEndpoint := fmt.Sprintf("/rest/api/2/user?accountId=%s", accountId)
req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, nil)
if err != nil {
return nil, err
}
@ -113,12 +147,18 @@ func (s *UserService) Delete(username string) (*Response, error) {
return resp, nil
}
// GetGroups returns the groups which the user belongs to
// Delete wraps DeleteWithContext using the background context.
// Caller must close resp.Body
func (s *UserService) Delete(accountId string) (*Response, error) {
return s.DeleteWithContext(context.Background(), accountId)
}
// GetGroupsWithContext returns the groups which the user belongs to
//
// JIRA API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/user-getUserGroups
func (s *UserService) GetGroups(username string) (*[]UserGroup, *Response, error) {
apiEndpoint := fmt.Sprintf("/rest/api/2/user/groups?username=%s", username)
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-user-groups-get
func (s *UserService) GetGroupsWithContext(ctx context.Context, accountId string) (*[]UserGroup, *Response, error) {
apiEndpoint := fmt.Sprintf("/rest/api/2/user/groups?accountId=%s", accountId)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
@ -131,12 +171,17 @@ func (s *UserService) GetGroups(username string) (*[]UserGroup, *Response, error
return userGroups, resp, nil
}
// Get information about the current logged-in user
// GetGroups wraps GetGroupsWithContext using the background context.
func (s *UserService) GetGroups(accountId string) (*[]UserGroup, *Response, error) {
return s.GetGroupsWithContext(context.Background(), accountId)
}
// GetSelfWithContext information about the current logged-in user
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-myself-get
func (s *UserService) GetSelf() (*User, *Response, error) {
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-myself-get
func (s *UserService) GetSelfWithContext(ctx context.Context) (*User, *Response, error) {
const apiEndpoint = "rest/api/2/myself"
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
@ -148,6 +193,11 @@ func (s *UserService) GetSelf() (*User, *Response, error) {
return &user, resp, nil
}
// GetSelf wraps GetSelfWithContext using the background context.
func (s *UserService) GetSelf() (*User, *Response, error) {
return s.GetSelfWithContext(context.Background())
}
// WithMaxResults sets the max results to return
func WithMaxResults(maxResults int) userSearchF {
return func(s userSearch) userSearch {
@ -180,14 +230,38 @@ func WithInactive(inactive bool) userSearchF {
}
}
// Find searches for user info from JIRA:
// It can find users by email, username or name
// WithUsername sets the username to search
func WithUsername(username string) userSearchF {
return func(s userSearch) userSearch {
s = append(s, userSearchParam{name: "username", value: username})
return s
}
}
// WithAccountId sets the account id to search
func WithAccountId(accountId string) userSearchF {
return func(s userSearch) userSearch {
s = append(s, userSearchParam{name: "accountId", value: accountId})
return s
}
}
// WithProperty sets the property (Property keys are specified by path) to search
func WithProperty(property string) userSearchF {
return func(s userSearch) userSearch {
s = append(s, userSearchParam{name: "property", value: property})
return s
}
}
// FindWithContext searches for user info from Jira:
// It can find users by email or display name using the query parameter
//
// JIRA API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/user-findUsers
func (s *UserService) Find(property string, tweaks ...userSearchF) ([]User, *Response, error) {
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-user-search-get
func (s *UserService) FindWithContext(ctx context.Context, property string, tweaks ...userSearchF) ([]User, *Response, error) {
search := []userSearchParam{
{
name: "username",
name: "query",
value: property,
},
}
@ -201,7 +275,7 @@ func (s *UserService) Find(property string, tweaks ...userSearchF) ([]User, *Res
}
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.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
@ -213,3 +287,8 @@ func (s *UserService) Find(property string, tweaks ...userSearchF) ([]User, *Res
}
return users, resp, nil
}
// Find wraps FindWithContext using the background context.
func (s *UserService) Find(property string, tweaks ...userSearchF) ([]User, *Response, error) {
return s.FindWithContext(context.Background(), property, tweaks...)
}

View file

@ -1,14 +1,15 @@
package jira
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
)
// VersionService handles Versions for the JIRA instance / API.
// VersionService handles Versions for the Jira instance / API.
//
// JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/version
// Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/version
type VersionService struct {
client *Client
}
@ -19,20 +20,20 @@ type Version struct {
ID string `json:"id,omitempty" structs:"id,omitempty"`
Name string `json:"name,omitempty" structs:"name,omitempty"`
Description string `json:"description,omitempty" structs:"description,omitempty"`
Archived bool `json:"archived,omitempty" structs:"archived,omitempty"`
Released bool `json:"released,omitempty" structs:"released,omitempty"`
Archived *bool `json:"archived,omitempty" structs:"archived,omitempty"`
Released *bool `json:"released,omitempty" structs:"released,omitempty"`
ReleaseDate string `json:"releaseDate,omitempty" structs:"releaseDate,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"`
}
// Get gets version info from JIRA
// GetWithContext gets version info from Jira
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-version-id-get
func (s *VersionService) Get(versionID int) (*Version, *Response, error) {
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-version-id-get
func (s *VersionService) GetWithContext(ctx context.Context, versionID int) (*Version, *Response, error) {
apiEndpoint := fmt.Sprintf("/rest/api/2/version/%v", versionID)
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
@ -45,12 +46,17 @@ func (s *VersionService) Get(versionID int) (*Version, *Response, error) {
return version, resp, nil
}
// Create creates a version in JIRA.
// Get wraps GetWithContext using the background context.
func (s *VersionService) Get(versionID int) (*Version, *Response, error) {
return s.GetWithContext(context.Background(), versionID)
}
// CreateWithContext creates a version in Jira.
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-version-post
func (s *VersionService) Create(version *Version) (*Version, *Response, error) {
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-version-post
func (s *VersionService) CreateWithContext(ctx context.Context, version *Version) (*Version, *Response, error) {
apiEndpoint := "/rest/api/2/version"
req, err := s.client.NewRequest("POST", apiEndpoint, version)
req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, version)
if err != nil {
return nil, nil, err
}
@ -64,23 +70,29 @@ func (s *VersionService) Create(version *Version) (*Version, *Response, error) {
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
e := fmt.Errorf("Could not read the returned data")
e := fmt.Errorf("could not read the returned data")
return nil, resp, NewJiraError(resp, e)
}
err = json.Unmarshal(data, responseVersion)
if err != nil {
e := fmt.Errorf("Could not unmarshall the data into struct")
e := fmt.Errorf("could not unmarshall the data into struct")
return nil, resp, NewJiraError(resp, e)
}
return responseVersion, resp, nil
}
// Update updates a version from a JSON representation.
// Create wraps CreateWithContext using the background context.
func (s *VersionService) Create(version *Version) (*Version, *Response, error) {
return s.CreateWithContext(context.Background(), version)
}
// UpdateWithContext updates a version from a JSON representation.
//
// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-version-id-put
func (s *VersionService) Update(version *Version) (*Version, *Response, error) {
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-version-id-put
// Caller must close resp.Body
func (s *VersionService) UpdateWithContext(ctx context.Context, version *Version) (*Version, *Response, error) {
apiEndpoint := fmt.Sprintf("rest/api/2/version/%v", version.ID)
req, err := s.client.NewRequest("PUT", apiEndpoint, version)
req, err := s.client.NewRequestWithContext(ctx, "PUT", apiEndpoint, version)
if err != nil {
return nil, nil, err
}
@ -95,3 +107,9 @@ func (s *VersionService) Update(version *Version) (*Version, *Response, error) {
ret := *version
return &ret, resp, nil
}
// Update wraps UpdateWithContext using the background context.
// Caller must close resp.Body
func (s *VersionService) Update(version *Version) (*Version, *Response, error) {
return s.UpdateWithContext(context.Background(), version)
}