From e1f4265e2b467b938fe0c095caf6d36f3136d2ff Mon Sep 17 00:00:00 2001 From: Suhaib Mujahid Date: Sun, 3 May 2020 09:38:32 -0400 Subject: [PATCH] feat(context): Add support for context package --- authentication.go | 39 ++++-- board.go | 76 ++++++++--- component.go | 13 +- field.go | 13 +- filter.go | 60 ++++++--- group.go | 45 +++++-- issue.go | 306 ++++++++++++++++++++++++++++++++------------ issuelinktype.go | 56 +++++--- jira.go | 34 +++-- metaissue.go | 34 +++-- permissionscheme.go | 27 +++- priority.go | 13 +- project.go | 45 +++++-- request_context.go | 23 ++++ request_legacy.go | 28 ++++ resolution.go | 13 +- role.go | 23 +++- sprint.go | 34 +++-- status.go | 13 +- statuscategory.go | 13 +- user.go | 78 ++++++++--- version.go | 34 +++-- 22 files changed, 766 insertions(+), 254 deletions(-) create mode 100644 request_context.go create mode 100644 request_legacy.go diff --git a/authentication.go b/authentication.go index 4884a7d..734aaa0 100644 --- a/authentication.go +++ b/authentication.go @@ -1,6 +1,7 @@ package jira import ( + "context" "encoding/json" "fmt" "io/ioutil" @@ -47,7 +48,7 @@ type Session struct { Cookies []*http.Cookie } -// AcquireSessionCookie creates a new session for a user in JIRA. +// 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. @@ -56,7 +57,7 @@ type Session struct { // 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 } @@ -91,6 +92,13 @@ func (s *AuthenticationService) AcquireSessionCookie(username, password string) return true, nil } +// 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 @@ -113,19 +121,19 @@ 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 // // 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) } @@ -145,10 +153,18 @@ func (s *AuthenticationService) Logout() error { } -// GetCurrentUser gets the details of the current user. +// Logout wraps LogoutWithContext using the background context. +// +// 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) GetCurrentUser() (*Session, error) { +func (s *AuthenticationService) GetCurrentUserWithContext(ctx context.Context) (*Session, error) { if s == nil { return nil, fmt.Errorf("authenticaiton Service is not instantiated") } @@ -157,7 +173,7 @@ func (s *AuthenticationService) GetCurrentUser() (*Session, error) { } 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) } @@ -185,3 +201,8 @@ func (s *AuthenticationService) GetCurrentUser() (*Session, error) { return ret, nil } + +// GetCurrentUser wraps GetCurrentUserWithContext using the background context. +func (s *AuthenticationService) GetCurrentUser() (*Session, error) { + return s.GetCurrentUserWithContext(context.Background()) +} diff --git a/board.go b/board.go index 3daa4c8..3b44226 100644 --- a/board.go +++ b/board.go @@ -1,6 +1,7 @@ package jira import ( + "context" "fmt" "strconv" "time" @@ -124,16 +125,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) { +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 +149,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) { +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,7 +175,12 @@ 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. @@ -177,9 +188,9 @@ func (s *BoardService) GetBoard(boardID int) (*Board, *Response, error) { // 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) { +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 +205,17 @@ 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) { +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 +227,16 @@ 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. +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) { +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 +250,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) { +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 +279,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 +304,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) +} diff --git a/component.go b/component.go index 407ad36..9fb3952 100644 --- a/component.go +++ b/component.go @@ -1,5 +1,7 @@ package jira +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 @@ -19,10 +21,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 +38,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) +} diff --git a/field.go b/field.go index 257d4f9..f73de85 100644 --- a/field.go +++ b/field.go @@ -1,5 +1,7 @@ package jira +import "context" + // FieldService handles fields for the JIRA instance / API. // // JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-Field @@ -24,12 +26,12 @@ type FieldSchema struct { System string `json:"system,omitempty" structs:"system,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) { +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 +43,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()) +} diff --git a/filter.go b/filter.go index 666153a..fecfcff 100644 --- a/filter.go +++ b/filter.go @@ -1,6 +1,9 @@ package jira -import "github.com/google/go-querystring/query" +import ( + "context" + "github.com/google/go-querystring/query" +) import "fmt" // FilterService handles fields for the JIRA instance / API. @@ -116,12 +119,12 @@ 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 } @@ -143,10 +146,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 +167,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 +189,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 +217,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) { +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 +245,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) +} diff --git a/group.go b/group.go index a0b7776..47b6810 100644 --- a/group.go +++ b/group.go @@ -1,6 +1,7 @@ package jira import ( + "context" "fmt" "net/url" ) @@ -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 // // 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) { +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) { +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,17 @@ 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) { +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 +170,8 @@ func (s *GroupService) Remove(groupname string, username string) (*Response, err return resp, nil } + +// Remove wraps RemoveWithContext using the background context. +func (s *GroupService) Remove(groupname string, username string) (*Response, error) { + return s.RemoveWithContext(context.Background(), groupname, username) +} diff --git a/issue.go b/issue.go index e958f2a..012a70c 100644 --- a/issue.go +++ b/issue.go @@ -2,6 +2,7 @@ package jira import ( "bytes" + "context" "encoding/json" "fmt" "io" @@ -588,7 +589,7 @@ type RemoteLinkStatus struct { Icon *RemoteLinkIcon } -// Get returns a full representation of the issue for the given issue key. +// GetWithContext 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. @@ -596,9 +597,9 @@ type RemoteLinkStatus struct { // The given options will be appended to the query string // // JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue-getIssue -func (s *IssueService) Get(issueID string, options *GetQueryOptions) (*Issue, *Response, error) { +func (s *IssueService) GetWithContext(ctx context.Context, issueID string, options *GetQueryOptions) (*Issue, *Response, error) { apiEndpoint := fmt.Sprintf("rest/api/2/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 } @@ -621,13 +622,18 @@ func (s *IssueService) Get(issueID string, options *GetQueryOptions) (*Issue, *R return issue, resp, nil } -// DownloadAttachment returns a Response of an attachment for a given attachmentID. +// Get wraps GetWithContext using the background context. +func (s *IssueService) Get(issueID string, options *GetQueryOptions) (*Issue, *Response, error) { + return s.GetWithContext(context.Background(), issueID, options) +} + +// DownloadAttachmentWithContext returns a Response of an attachment for a given attachmentID. // The attachment is in the Response.Body of the response. // This is an io.ReadCloser. // The caller should close the resp.Body. -func (s *IssueService) DownloadAttachment(attachmentID string) (*Response, error) { +func (s *IssueService) DownloadAttachmentWithContext(ctx context.Context, attachmentID string) (*Response, error) { apiEndpoint := fmt.Sprintf("secure/attachment/%s/", attachmentID) - req, err := s.client.NewRequest("GET", apiEndpoint, nil) + req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil) if err != nil { return nil, err } @@ -641,8 +647,13 @@ func (s *IssueService) DownloadAttachment(attachmentID string) (*Response, error return resp, nil } -// PostAttachment uploads r (io.Reader) as an attachment to a given issueID -func (s *IssueService) PostAttachment(issueID string, r io.Reader, attachmentName string) (*[]Attachment, *Response, error) { +// DownloadAttachment wraps DownloadAttachmentWithContext using the background context. +func (s *IssueService) DownloadAttachment(attachmentID string) (*Response, error) { + return s.DownloadAttachmentWithContext(context.Background(), attachmentID) +} + +// PostAttachmentWithContext uploads r (io.Reader) as an attachment to a given issueID +func (s *IssueService) PostAttachmentWithContext(ctx context.Context, issueID string, r io.Reader, attachmentName string) (*[]Attachment, *Response, error) { apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/attachments", issueID) b := new(bytes.Buffer) @@ -661,7 +672,7 @@ func (s *IssueService) PostAttachment(issueID string, r io.Reader, attachmentNam } writer.Close() - req, err := s.client.NewMultiPartRequest("POST", apiEndpoint, b) + req, err := s.client.NewMultiPartRequestWithContext(ctx, "POST", apiEndpoint, b) if err != nil { return nil, nil, err } @@ -679,11 +690,16 @@ func (s *IssueService) PostAttachment(issueID string, r io.Reader, attachmentNam return attachment, resp, nil } -// DeleteAttachment deletes an attachment of a given attachmentID -func (s *IssueService) DeleteAttachment(attachmentID string) (*Response, error) { +// PostAttachment wraps PostAttachmentWithContext using the background context. +func (s *IssueService) PostAttachment(issueID string, r io.Reader, attachmentName string) (*[]Attachment, *Response, error) { + return s.PostAttachmentWithContext(context.Background(), issueID, r, attachmentName) +} + +// DeleteAttachmentWithContext deletes an attachment of a given attachmentID +func (s *IssueService) DeleteAttachmentWithContext(ctx context.Context, attachmentID string) (*Response, error) { apiEndpoint := fmt.Sprintf("rest/api/2/attachment/%s", attachmentID) - req, err := s.client.NewRequest("DELETE", apiEndpoint, nil) + req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, nil) if err != nil { return nil, err } @@ -697,14 +713,19 @@ func (s *IssueService) DeleteAttachment(attachmentID string) (*Response, error) return resp, nil } -// GetWorklogs gets all the worklogs for an issue. +// DeleteAttachment wraps DeleteAttachmentWithContext using the background context. +func (s *IssueService) DeleteAttachment(attachmentID string) (*Response, error) { + return s.DeleteAttachmentWithContext(context.Background(), attachmentID) +} + +// GetWorklogsWithContext gets all the worklogs for an issue. // This method is especially important if you need to read all the worklogs, not just the first page. // // https://docs.atlassian.com/jira/REST/cloud/#api/2/issue/{issueIdOrKey}/worklog-getIssueWorklog -func (s *IssueService) GetWorklogs(issueID string, options ...func(*http.Request) error) (*Worklog, *Response, error) { +func (s *IssueService) GetWorklogsWithContext(ctx context.Context, issueID string, options ...func(*http.Request) error) (*Worklog, *Response, error) { apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/worklog", 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 } @@ -721,6 +742,11 @@ func (s *IssueService) GetWorklogs(issueID string, options ...func(*http.Request return v, resp, err } +// GetWorklogs wraps GetWorklogsWithContext using the background context. +func (s *IssueService) GetWorklogs(issueID string, options ...func(*http.Request) error) (*Worklog, *Response, error) { + return s.GetWorklogsWithContext(context.Background(), issueID, options...) +} + // Applies query options to http request. // This helper is meant to be used with all "QueryOptions" structs. func WithQueryOptions(options interface{}) func(*http.Request) error { @@ -737,14 +763,14 @@ func WithQueryOptions(options interface{}) func(*http.Request) error { } } -// Create creates an issue or a sub-task from a JSON representation. +// CreateWithContext creates an issue or a sub-task from a JSON representation. // Creating a sub-task is similar to creating a regular issue, with two important differences: // The issueType field must correspond to a sub-task issue type and you must provide a parent field in the issue create request containing the id or key of the parent issue. // // JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue-createIssues -func (s *IssueService) Create(issue *Issue) (*Issue, *Response, error) { +func (s *IssueService) CreateWithContext(ctx context.Context, issue *Issue) (*Issue, *Response, error) { apiEndpoint := "rest/api/2/issue" - req, err := s.client.NewRequest("POST", apiEndpoint, issue) + req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, issue) if err != nil { return nil, nil, err } @@ -767,17 +793,22 @@ func (s *IssueService) Create(issue *Issue) (*Issue, *Response, error) { return responseIssue, resp, nil } -// UpdateWithOptions updates an issue from a JSON representation, +// Create wraps CreateWithContext using the background context. +func (s *IssueService) Create(issue *Issue) (*Issue, *Response, error) { + return s.CreateWithContext(context.Background(), issue) +} + +// UpdateWithOptionsWithContext updates an issue from a JSON representation, // while also specifiying query params. The issue is found by key. // // JIRA API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/issue-editIssue -func (s *IssueService) UpdateWithOptions(issue *Issue, opts *UpdateQueryOptions) (*Issue, *Response, error) { +func (s *IssueService) UpdateWithOptionsWithContext(ctx context.Context, issue *Issue, opts *UpdateQueryOptions) (*Issue, *Response, error) { apiEndpoint := fmt.Sprintf("rest/api/2/issue/%v", issue.Key) url, err := addOptions(apiEndpoint, opts) if err != nil { return nil, nil, err } - req, err := s.client.NewRequest("PUT", url, issue) + req, err := s.client.NewRequestWithContext(ctx, "PUT", url, issue) if err != nil { return nil, nil, err } @@ -793,19 +824,29 @@ func (s *IssueService) UpdateWithOptions(issue *Issue, opts *UpdateQueryOptions) return &ret, resp, nil } -// Update updates an issue from a JSON representation. The issue is found by key. +// UpdateWithOptions wraps UpdateWithOptionsWithContext using the background context. +func (s *IssueService) UpdateWithOptions(issue *Issue, opts *UpdateQueryOptions) (*Issue, *Response, error) { + return s.UpdateWithOptionsWithContext(context.Background(), issue, opts) +} + +// UpdateWithContext updates an issue from a JSON representation. The issue is found by key. // // JIRA API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/issue-editIssue -func (s *IssueService) Update(issue *Issue) (*Issue, *Response, error) { +func (s *IssueService) UpdateWithContext(ctx context.Context, issue *Issue) (*Issue, *Response, error) { return s.UpdateWithOptions(issue, nil) } -// UpdateIssue updates an issue from a JSON representation. The issue is found by key. +// Update wraps UpdateWithContext using the background context. +func (s *IssueService) Update(issue *Issue) (*Issue, *Response, error) { + return s.UpdateWithContext(context.Background(), issue) +} + +// UpdateIssueWithContext updates an issue from a JSON representation. The issue is found by key. // // https://docs.atlassian.com/jira/REST/7.4.0/#api/2/issue-editIssue -func (s *IssueService) UpdateIssue(jiraID string, data map[string]interface{}) (*Response, error) { +func (s *IssueService) UpdateIssueWithContext(ctx context.Context, jiraID string, data map[string]interface{}) (*Response, error) { apiEndpoint := fmt.Sprintf("rest/api/2/issue/%v", jiraID) - req, err := s.client.NewRequest("PUT", apiEndpoint, data) + req, err := s.client.NewRequestWithContext(ctx, "PUT", apiEndpoint, data) if err != nil { return nil, err } @@ -819,12 +860,17 @@ func (s *IssueService) UpdateIssue(jiraID string, data map[string]interface{}) ( return resp, nil } -// AddComment adds a new comment to issueID. +// UpdateIssue wraps UpdateIssueWithContext using the background context. +func (s *IssueService) UpdateIssue(jiraID string, data map[string]interface{}) (*Response, error) { + return s.UpdateIssueWithContext(context.Background(), jiraID, data) +} + +// AddCommentWithContext adds a new comment to issueID. // // JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue-addComment -func (s *IssueService) AddComment(issueID string, comment *Comment) (*Comment, *Response, error) { +func (s *IssueService) AddCommentWithContext(ctx context.Context, issueID string, comment *Comment) (*Comment, *Response, error) { apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/comment", issueID) - req, err := s.client.NewRequest("POST", apiEndpoint, comment) + req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, comment) if err != nil { return nil, nil, err } @@ -839,17 +885,22 @@ func (s *IssueService) AddComment(issueID string, comment *Comment) (*Comment, * return responseComment, resp, nil } -// UpdateComment updates the body of a comment, identified by comment.ID, on the issueID. +// AddComment wraps AddCommentWithContext using the background context. +func (s *IssueService) AddComment(issueID string, comment *Comment) (*Comment, *Response, error) { + return s.AddCommentWithContext(context.Background(), issueID, comment) +} + +// UpdateCommentWithContext updates the body of a comment, identified by comment.ID, on the issueID. // // JIRA API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/issue/{issueIdOrKey}/comment-updateComment -func (s *IssueService) UpdateComment(issueID string, comment *Comment) (*Comment, *Response, error) { +func (s *IssueService) UpdateCommentWithContext(ctx context.Context, issueID string, comment *Comment) (*Comment, *Response, error) { reqBody := struct { Body string `json:"body"` }{ Body: comment.Body, } apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/comment/%s", issueID, comment.ID) - req, err := s.client.NewRequest("PUT", apiEndpoint, reqBody) + req, err := s.client.NewRequestWithContext(ctx, "PUT", apiEndpoint, reqBody) if err != nil { return nil, nil, err } @@ -863,12 +914,17 @@ func (s *IssueService) UpdateComment(issueID string, comment *Comment) (*Comment return responseComment, resp, nil } -// DeleteComment Deletes a comment from an issueID. +// UpdateComment wraps UpdateCommentWithContext using the background context. +func (s *IssueService) UpdateComment(issueID string, comment *Comment) (*Comment, *Response, error) { + return s.UpdateCommentWithContext(context.Background(), issueID, comment) +} + +// DeleteCommentWithContext Deletes a comment from an issueID. // // JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-issue-issueIdOrKey-comment-id-delete -func (s *IssueService) DeleteComment(issueID, commentID string) error { +func (s *IssueService) DeleteCommentWithContext(ctx context.Context, issueID, commentID string) error { apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/comment/%s", issueID, commentID) - req, err := s.client.NewRequest("DELETE", apiEndpoint, nil) + req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, nil) if err != nil { return err } @@ -882,12 +938,17 @@ func (s *IssueService) DeleteComment(issueID, commentID string) error { return nil } -// AddWorklogRecord adds a new worklog record to issueID. +// DeleteComment wraps DeleteCommentWithContext using the background context. +func (s *IssueService) DeleteComment(issueID, commentID string) error { + return s.DeleteCommentWithContext(context.Background(), issueID, commentID) +} + +// AddWorklogRecordWithContext adds a new worklog record to issueID. // // https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-issue-issueIdOrKey-worklog-post -func (s *IssueService) AddWorklogRecord(issueID string, record *WorklogRecord, options ...func(*http.Request) error) (*WorklogRecord, *Response, error) { +func (s *IssueService) AddWorklogRecordWithContext(ctx context.Context, issueID string, record *WorklogRecord, options ...func(*http.Request) error) (*WorklogRecord, *Response, error) { apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/worklog", issueID) - req, err := s.client.NewRequest("POST", apiEndpoint, record) + req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, record) if err != nil { return nil, nil, err } @@ -909,12 +970,17 @@ func (s *IssueService) AddWorklogRecord(issueID string, record *WorklogRecord, o return responseRecord, resp, nil } -// UpdateWorklogRecord updates a worklog record. +// AddWorklogRecord wraps AddWorklogRecordWithContext using the background context. +func (s *IssueService) AddWorklogRecord(issueID string, record *WorklogRecord, options ...func(*http.Request) error) (*WorklogRecord, *Response, error) { + return s.AddWorklogRecordWithContext(context.Background(), issueID, record, options...) +} + +// UpdateWorklogRecordWithContext updates a worklog record. // // https://docs.atlassian.com/software/jira/docs/api/REST/7.1.2/#api/2/issue-updateWorklog -func (s *IssueService) UpdateWorklogRecord(issueID, worklogID string, record *WorklogRecord, options ...func(*http.Request) error) (*WorklogRecord, *Response, error) { +func (s *IssueService) UpdateWorklogRecordWithContext(ctx context.Context, issueID, worklogID string, record *WorklogRecord, options ...func(*http.Request) error) (*WorklogRecord, *Response, error) { apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/worklog/%s", issueID, worklogID) - req, err := s.client.NewRequest("PUT", apiEndpoint, record) + req, err := s.client.NewRequestWithContext(ctx, "PUT", apiEndpoint, record) if err != nil { return nil, nil, err } @@ -936,12 +1002,17 @@ func (s *IssueService) UpdateWorklogRecord(issueID, worklogID string, record *Wo return responseRecord, resp, nil } -// AddLink adds a link between two issues. +// UpdateWorklogRecord wraps UpdateWorklogRecordWithContext using the background context. +func (s *IssueService) UpdateWorklogRecord(issueID, worklogID string, record *WorklogRecord, options ...func(*http.Request) error) (*WorklogRecord, *Response, error) { + return s.UpdateWorklogRecordWithContext(context.Background(), issueID, worklogID, record, options...) +} + +// AddLinkWithContext adds a link between two issues. // // JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issueLink -func (s *IssueService) AddLink(issueLink *IssueLink) (*Response, error) { +func (s *IssueService) AddLinkWithContext(ctx context.Context, issueLink *IssueLink) (*Response, error) { apiEndpoint := "rest/api/2/issueLink" - req, err := s.client.NewRequest("POST", apiEndpoint, issueLink) + req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, issueLink) if err != nil { return nil, err } @@ -954,10 +1025,15 @@ func (s *IssueService) AddLink(issueLink *IssueLink) (*Response, error) { return resp, err } -// Search will search for tickets according to the jql +// AddLink wraps AddLinkWithContext using the background context. +func (s *IssueService) AddLink(issueLink *IssueLink) (*Response, error) { + return s.AddLinkWithContext(context.Background(), issueLink) +} + +// SearchWithContext will search for tickets according to the jql // // JIRA API docs: https://developer.atlassian.com/jiradev/jira-apis/jira-rest-apis/jira-rest-api-tutorials/jira-rest-api-example-query-issues -func (s *IssueService) Search(jql string, options *SearchOptions) ([]Issue, *Response, error) { +func (s *IssueService) SearchWithContext(ctx context.Context, jql string, options *SearchOptions) ([]Issue, *Response, error) { u := url.URL{ Path: "rest/api/2/search", } @@ -967,6 +1043,7 @@ func (s *IssueService) Search(jql string, options *SearchOptions) ([]Issue, *Res } if options != nil { + if options.StartAt != 0 { uv.Add("startAt", strconv.Itoa(options.StartAt)) } @@ -986,7 +1063,7 @@ func (s *IssueService) Search(jql string, options *SearchOptions) ([]Issue, *Res u.RawQuery = uv.Encode() - req, err := s.client.NewRequest("GET", u.String(), nil) + req, err := s.client.NewRequestWithContext(ctx, "GET", u.String(), nil) if err != nil { return []Issue{}, nil, err } @@ -999,10 +1076,15 @@ func (s *IssueService) Search(jql string, options *SearchOptions) ([]Issue, *Res return v.Issues, resp, err } -// SearchPages will get issues from all pages in a search +// Search wraps SearchWithContext using the background context. +func (s *IssueService) Search(jql string, options *SearchOptions) ([]Issue, *Response, error) { + return s.SearchWithContext(context.Background(), jql, options) +} + +// SearchPagesWithContext will get issues from all pages in a search // // JIRA API docs: https://developer.atlassian.com/jiradev/jira-apis/jira-rest-apis/jira-rest-api-tutorials/jira-rest-api-example-query-issues -func (s *IssueService) SearchPages(jql string, options *SearchOptions, f func(Issue) error) error { +func (s *IssueService) SearchPagesWithContext(ctx context.Context, jql string, options *SearchOptions, f func(Issue) error) error { if options == nil { options = &SearchOptions{ StartAt: 0, @@ -1043,10 +1125,15 @@ func (s *IssueService) SearchPages(jql string, options *SearchOptions, f func(Is } } -// GetCustomFields returns a map of customfield_* keys with string values -func (s *IssueService) GetCustomFields(issueID string) (CustomFields, *Response, error) { +// SearchPages wraps SearchPagesWithContext using the background context. +func (s *IssueService) SearchPages(jql string, options *SearchOptions, f func(Issue) error) error { + return s.SearchPagesWithContext(context.Background(), jql, options, f) +} + +// GetCustomFieldsWithContext returns a map of customfield_* keys with string values +func (s *IssueService) GetCustomFieldsWithContext(ctx context.Context, issueID string) (CustomFields, *Response, error) { apiEndpoint := fmt.Sprintf("rest/api/2/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 } @@ -1080,13 +1167,18 @@ func (s *IssueService) GetCustomFields(issueID string) (CustomFields, *Response, return cf, resp, nil } -// GetTransitions gets a list of the transitions possible for this issue by the current user, +// GetCustomFields wraps GetCustomFieldsWithContext using the background context. +func (s *IssueService) GetCustomFields(issueID string) (CustomFields, *Response, error) { + return s.GetCustomFieldsWithContext(context.Background(), issueID) +} + +// GetTransitionsWithContext gets a list of the transitions possible for this issue by the current user, // along with fields that are required and their types. // // JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue-getTransitions -func (s *IssueService) GetTransitions(id string) ([]Transition, *Response, error) { +func (s *IssueService) GetTransitionsWithContext(ctx context.Context, id string) ([]Transition, *Response, error) { apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/transitions?expand=transitions.fields", 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 } @@ -1099,27 +1191,37 @@ func (s *IssueService) GetTransitions(id string) ([]Transition, *Response, error return result.Transitions, resp, err } -// DoTransition performs a transition on an issue. +// GetTransitions wraps GetTransitionsWithContext using the background context. +func (s *IssueService) GetTransitions(id string) ([]Transition, *Response, error) { + return s.GetTransitionsWithContext(context.Background(), id) +} + +// DoTransitionWithContext performs a transition on an issue. // When performing the transition you can update or set other issue fields. // // JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue-doTransition -func (s *IssueService) DoTransition(ticketID, transitionID string) (*Response, error) { +func (s *IssueService) DoTransitionWithContext(ctx context.Context, ticketID, transitionID string) (*Response, error) { payload := CreateTransitionPayload{ Transition: TransitionPayload{ ID: transitionID, }, } - return s.DoTransitionWithPayload(ticketID, payload) + return s.DoTransitionWithPayloadWithContext(ctx, ticketID, payload) } -// DoTransitionWithPayload performs a transition on an issue using any payload. +// DoTransition wraps DoTransitionWithContext using the background context. +func (s *IssueService) DoTransition(ticketID, transitionID string) (*Response, error) { + return s.DoTransitionWithContext(context.Background(), ticketID, transitionID) +} + +// DoTransitionWithPayloadWithContext performs a transition on an issue using any payload. // When performing the transition you can update or set other issue fields. // // JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue-doTransition -func (s *IssueService) DoTransitionWithPayload(ticketID, payload interface{}) (*Response, error) { +func (s *IssueService) DoTransitionWithPayloadWithContext(ctx context.Context, ticketID, payload interface{}) (*Response, error) { apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/transitions", ticketID) - req, err := s.client.NewRequest("POST", apiEndpoint, payload) + req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, payload) if err != nil { return nil, err } @@ -1132,6 +1234,11 @@ func (s *IssueService) DoTransitionWithPayload(ticketID, payload interface{}) (* return resp, err } +// DoTransitionWithPayload wraps DoTransitionWithPayloadWithContext using the background context. +func (s *IssueService) DoTransitionWithPayload(ticketID, payload interface{}) (*Response, error) { + return s.DoTransitionWithPayloadWithContext(context.Background(), ticketID, payload) +} + // InitIssueWithMetaAndFields returns Issue with with values from fieldsConfig properly set. // * metaProject should contain metaInformation about the project where the issue should be created. // * metaIssuetype is the MetaInformation about the Issuetype that needs to be created. @@ -1211,8 +1318,8 @@ func InitIssueWithMetaAndFields(metaProject *MetaProject, metaIssuetype *MetaIss return issue, nil } -// Delete will delete a specified issue. -func (s *IssueService) Delete(issueID string) (*Response, error) { +// DeleteWithContext will delete a specified issue. +func (s *IssueService) DeleteWithContext(ctx context.Context, issueID string) (*Response, error) { apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s", issueID) // to enable deletion of subtasks; without this, the request will fail if the issue has subtasks @@ -1220,7 +1327,7 @@ func (s *IssueService) Delete(issueID string) (*Response, error) { deletePayload["deleteSubtasks"] = "true" content, _ := json.Marshal(deletePayload) - req, err := s.client.NewRequest("DELETE", apiEndpoint, content) + req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, content) if err != nil { return nil, err } @@ -1229,13 +1336,18 @@ func (s *IssueService) Delete(issueID string) (*Response, error) { return resp, err } -// GetWatchers wil return all the users watching/observing the given issue +// Delete wraps DeleteWithContext using the background context. +func (s *IssueService) Delete(issueID string) (*Response, error) { + return s.DeleteWithContext(context.Background(), issueID) +} + +// GetWatchersWithContext wil return all the users watching/observing the given issue // // JIRA API docs: https://docs.atlassian.com/software/jira/docs/api/REST/latest/#api/2/issue-getIssueWatchers -func (s *IssueService) GetWatchers(issueID string) (*[]User, *Response, error) { +func (s *IssueService) GetWatchersWithContext(ctx context.Context, issueID string) (*[]User, *Response, error) { watchesAPIEndpoint := fmt.Sprintf("rest/api/2/issue/%s/watchers", issueID) - req, err := s.client.NewRequest("GET", watchesAPIEndpoint, nil) + req, err := s.client.NewRequestWithContext(ctx, "GET", watchesAPIEndpoint, nil) if err != nil { return nil, nil, err } @@ -1267,13 +1379,18 @@ func (s *IssueService) GetWatchers(issueID string) (*[]User, *Response, error) { return &result, resp, nil } -// AddWatcher adds watcher to the given issue +// GetWatchers wraps GetWatchersWithContext using the background context. +func (s *IssueService) GetWatchers(issueID string) (*[]User, *Response, error) { + return s.GetWatchersWithContext(context.Background(), issueID) +} + +// AddWatcherWithContext adds watcher to the given issue // // JIRA API docs: https://docs.atlassian.com/software/jira/docs/api/REST/latest/#api/2/issue-addWatcher -func (s *IssueService) AddWatcher(issueID string, userName string) (*Response, error) { +func (s *IssueService) AddWatcherWithContext(ctx context.Context, issueID string, userName string) (*Response, error) { apiEndPoint := fmt.Sprintf("rest/api/2/issue/%s/watchers", issueID) - req, err := s.client.NewRequest("POST", apiEndPoint, userName) + req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndPoint, userName) if err != nil { return nil, err } @@ -1286,13 +1403,18 @@ func (s *IssueService) AddWatcher(issueID string, userName string) (*Response, e return resp, err } -// RemoveWatcher removes given user from given issue +// AddWatcher wraps AddWatcherWithContext using the background context. +func (s *IssueService) AddWatcher(issueID string, userName string) (*Response, error) { + return s.AddWatcherWithContext(context.Background(), issueID, userName) +} + +// RemoveWatcherWithContext removes given user from given issue // // JIRA API docs: https://docs.atlassian.com/software/jira/docs/api/REST/latest/#api/2/issue-removeWatcher -func (s *IssueService) RemoveWatcher(issueID string, userName string) (*Response, error) { +func (s *IssueService) RemoveWatcherWithContext(ctx context.Context, issueID string, userName string) (*Response, error) { apiEndPoint := fmt.Sprintf("rest/api/2/issue/%s/watchers", issueID) - req, err := s.client.NewRequest("DELETE", apiEndPoint, userName) + req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndPoint, userName) if err != nil { return nil, err } @@ -1305,13 +1427,18 @@ func (s *IssueService) RemoveWatcher(issueID string, userName string) (*Response return resp, err } -// UpdateAssignee updates the user assigned to work on the given issue +// RemoveWatcher wraps RemoveWatcherWithContext using the background context. +func (s *IssueService) RemoveWatcher(issueID string, userName string) (*Response, error) { + return s.RemoveWatcherWithContext(context.Background(), issueID, userName) +} + +// UpdateAssigneeWithContext updates the user assigned to work on the given issue // // JIRA API docs: https://docs.atlassian.com/software/jira/docs/api/REST/7.10.2/#api/2/issue-assign -func (s *IssueService) UpdateAssignee(issueID string, assignee *User) (*Response, error) { +func (s *IssueService) UpdateAssigneeWithContext(ctx context.Context, issueID string, assignee *User) (*Response, error) { apiEndPoint := fmt.Sprintf("rest/api/2/issue/%s/assignee", issueID) - req, err := s.client.NewRequest("PUT", apiEndPoint, assignee) + req, err := s.client.NewRequestWithContext(ctx, "PUT", apiEndPoint, assignee) if err != nil { return nil, err } @@ -1324,6 +1451,11 @@ func (s *IssueService) UpdateAssignee(issueID string, assignee *User) (*Response return resp, err } +// UpdateAssignee wraps UpdateAssigneeWithContext using the background context. +func (s *IssueService) UpdateAssignee(issueID string, assignee *User) (*Response, error) { + return s.UpdateAssigneeWithContext(context.Background(), issueID, assignee) +} + func (c ChangelogHistory) CreatedTime() (time.Time, error) { var t time.Time // Ignore null @@ -1334,12 +1466,12 @@ func (c ChangelogHistory) CreatedTime() (time.Time, error) { return t, err } -// GetRemoteLinks gets remote issue links on the issue. +// GetRemoteLinksWithContext gets remote issue links on the issue. // // JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue-getRemoteIssueLinks -func (s *IssueService) GetRemoteLinks(id string) (*[]RemoteLink, *Response, error) { +func (s *IssueService) GetRemoteLinksWithContext(ctx context.Context, id string) (*[]RemoteLink, *Response, error) { apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/remotelink", 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 } @@ -1352,12 +1484,17 @@ func (s *IssueService) GetRemoteLinks(id string) (*[]RemoteLink, *Response, erro return result, resp, err } -// AddRemoteLink adds a remote link to issueID. +// GetRemoteLinks wraps GetRemoteLinksWithContext using the background context. +func (s *IssueService) GetRemoteLinks(id string) (*[]RemoteLink, *Response, error) { + return s.GetRemoteLinksWithContext(context.Background(), id) +} + +// AddRemoteLinkWithContext adds a remote link to issueID. // // JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-issueIdOrKey-remotelink-post -func (s *IssueService) AddRemoteLink(issueID string, remotelink *RemoteLink) (*RemoteLink, *Response, error) { +func (s *IssueService) AddRemoteLinkWithContext(ctx context.Context, issueID string, remotelink *RemoteLink) (*RemoteLink, *Response, error) { apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/remotelink", issueID) - req, err := s.client.NewRequest("POST", apiEndpoint, remotelink) + req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, remotelink) if err != nil { return nil, nil, err } @@ -1371,3 +1508,8 @@ func (s *IssueService) AddRemoteLink(issueID string, remotelink *RemoteLink) (*R return responseRemotelink, resp, nil } + +// AddRemoteLink wraps AddRemoteLinkWithContext using the background context. +func (s *IssueService) AddRemoteLink(issueID string, remotelink *RemoteLink) (*RemoteLink, *Response, error) { + return s.AddRemoteLinkWithContext(context.Background(), issueID, remotelink) +} diff --git a/issuelinktype.go b/issuelinktype.go index 5afe3fb..52b3e38 100644 --- a/issuelinktype.go +++ b/issuelinktype.go @@ -1,6 +1,7 @@ package jira import ( + "context" "encoding/json" "fmt" "io/ioutil" @@ -13,12 +14,12 @@ 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) { +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) { +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) { +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 } @@ -79,12 +90,17 @@ func (s *IssueLinkTypeService) Create(linkType *IssueLinkType) (*IssueLinkType, 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) { +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 +112,17 @@ 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. +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) { +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 +130,8 @@ 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. +func (s *IssueLinkTypeService) Delete(ID string) (*Response, error) { + return s.DeleteWithContext(context.Background(), ID) +} diff --git a/jira.go b/jira.go index ef116f1..cc265bb 100644 --- a/jira.go +++ b/jira.go @@ -2,6 +2,7 @@ package jira import ( "bytes" + "context" "crypto/sha256" "encoding/hex" "encoding/json" @@ -105,10 +106,10 @@ func NewClient(httpClient httpClient, baseURL string) (*Client, error) { 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 +119,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 +144,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 +171,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 +196,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 +223,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 +236,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 +262,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) { diff --git a/metaissue.go b/metaissue.go index 4d5c6b0..560a2f0 100644 --- a/metaissue.go +++ b/metaissue.go @@ -1,6 +1,7 @@ package jira import ( + "context" "fmt" "strings" @@ -47,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 } @@ -78,11 +84,16 @@ func (s *IssueService) GetCreateMetaWithOptions(options *GetQueryOptions) (*Crea return meta, resp, nil } -// GetEditMeta makes the api call to get the edit meta information for an issue -func (s *IssueService) GetEditMeta(issue *Issue) (*EditMetaInfo, *Response, error) { +// 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.NewRequest("GET", apiEndpoint, nil) + req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil) if err != nil { return nil, nil, err } @@ -97,6 +108,11 @@ func (s *IssueService) GetEditMeta(issue *Issue) (*EditMetaInfo, *Response, erro 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 { diff --git a/permissionscheme.go b/permissionscheme.go index b242cf3..28e7072 100644 --- a/permissionscheme.go +++ b/permissionscheme.go @@ -1,6 +1,9 @@ package jira -import "fmt" +import ( + "context" + "fmt" +) // PermissionSchemeService handles permissionschemes for the JIRA instance / API. // @@ -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) { +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) { +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 } @@ -67,3 +75,8 @@ func (s *PermissionSchemeService) Get(schemeID int) (*PermissionScheme, *Respons 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) +} diff --git a/priority.go b/priority.go index 481f959..9e59e5a 100644 --- a/priority.go +++ b/priority.go @@ -1,5 +1,7 @@ package jira +import "context" + // PriorityService handles priorities for the JIRA instance / API. // // JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-Priority @@ -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) { +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()) +} diff --git a/project.go b/project.go index 2418422..ea229b9 100644 --- a/project.go +++ b/project.go @@ -1,6 +1,7 @@ package jira import ( + "context" "fmt" "github.com/google/go-querystring/query" @@ -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{}) +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) { +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. +// 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) { +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 +// 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) { +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) +} diff --git a/request_context.go b/request_context.go new file mode 100644 index 0000000..fc0052d --- /dev/null +++ b/request_context.go @@ -0,0 +1,23 @@ +// +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) +} diff --git a/request_legacy.go b/request_legacy.go new file mode 100644 index 0000000..5ceee88 --- /dev/null +++ b/request_legacy.go @@ -0,0 +1,28 @@ +// +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 +} diff --git a/resolution.go b/resolution.go index 36a651f..6e56100 100644 --- a/resolution.go +++ b/resolution.go @@ -1,5 +1,7 @@ package jira +import "context" + // ResolutionService handles resolutions for the JIRA instance / API. // // JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-Resolution @@ -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) { +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()) +} diff --git a/role.go b/role.go index 4ad663f..802c799 100644 --- a/role.go +++ b/role.go @@ -1,6 +1,7 @@ package jira import ( + "context" "fmt" ) @@ -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) { +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) { +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 } @@ -74,3 +80,8 @@ func (s *RoleService) Get(roleID int) (*Role, *Response, error) { 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) +} diff --git a/sprint.go b/sprint.go index 7e8e697..2c27719 100644 --- a/sprint.go +++ b/sprint.go @@ -1,6 +1,7 @@ package jira import ( + "context" "fmt" "github.com/google/go-querystring/query" @@ -22,17 +23,17 @@ 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) { +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 +46,20 @@ 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. +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) { +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,7 +74,12 @@ 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. +// 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. @@ -78,10 +89,10 @@ func (s *SprintService) GetIssuesForSprint(sprintID int) ([]Issue, *Response, er // 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 +116,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) +} diff --git a/status.go b/status.go index 365d958..a11156a 100644 --- a/status.go +++ b/status.go @@ -1,5 +1,7 @@ package jira +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 @@ -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) { +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()) +} diff --git a/statuscategory.go b/statuscategory.go index 05db420..3018651 100644 --- a/statuscategory.go +++ b/statuscategory.go @@ -1,5 +1,7 @@ package jira +import "context" + // StatusCategoryService handles status categories for the JIRA instance / API. // // JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-Statuscategory @@ -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) { +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()) +} \ No newline at end of file diff --git a/user.go b/user.go index b77314f..5a73ed7 100644 --- a/user.go +++ b/user.go @@ -1,6 +1,7 @@ package jira import ( + "context" "encoding/json" "fmt" "io/ioutil" @@ -47,16 +48,16 @@ type userSearch []userSearchParam type userSearchF func(userSearch) userSearch -// Get gets user info from JIRA +// GetWithContext gets user info from JIRA // // JIRA API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/user-getUser // // /!\ Deprecation notice: https://developer.atlassian.com/cloud/jira/platform/deprecation-notice-user-privacy-api-migration-guide/ // By 29 April 2019, we will remove personal data from the API that is used to identify users, // such as username and userKey, and instead use the Atlassian account ID (accountId). -func (s *UserService) Get(username string) (*User, *Response, error) { +func (s *UserService) GetWithContext(ctx context.Context, username string) (*User, *Response, error) { apiEndpoint := fmt.Sprintf("/rest/api/2/user?username=%s", username) - 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,12 +70,17 @@ func (s *UserService) Get(username string) (*User, *Response, error) { return user, resp, nil } -// Get gets user info from JIRA +// Get wraps GetWithContext using the background context. +func (s *UserService) Get(username string) (*User, *Response, error) { + return s.GetWithContext(context.Background(), username) +} + +// GetByAccountIDWithContext gets user info from JIRA // // JIRA API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/user-getUser -func (s *UserService) GetByAccountID(accountID string) (*User, *Response, error) { +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.NewRequest("GET", apiEndpoint, nil) + req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil) if err != nil { return nil, nil, err } @@ -87,12 +93,17 @@ func (s *UserService) GetByAccountID(accountID string) (*User, *Response, error) return user, resp, nil } -// Create creates an user in JIRA. +// 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) { +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 } @@ -117,13 +128,18 @@ func (s *UserService) Create(user *User) (*User, *Response, error) { 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) { +func (s *UserService) DeleteWithContext(ctx context.Context, username string) (*Response, error) { apiEndpoint := fmt.Sprintf("/rest/api/2/user?username=%s", username) - req, err := s.client.NewRequest("DELETE", apiEndpoint, nil) + req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, nil) if err != nil { return nil, err } @@ -135,12 +151,17 @@ 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. +func (s *UserService) Delete(username string) (*Response, error) { + return s.DeleteWithContext(context.Background(), username) +} + +// 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) { +func (s *UserService) GetGroupsWithContext(ctx context.Context, username string) (*[]UserGroup, *Response, error) { apiEndpoint := fmt.Sprintf("/rest/api/2/user/groups?username=%s", username) - req, err := s.client.NewRequest("GET", apiEndpoint, nil) + req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil) if err != nil { return nil, nil, err } @@ -153,12 +174,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(username string) (*[]UserGroup, *Response, error) { + return s.GetGroupsWithContext(context.Background(), username) +} + +// 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) { +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 } @@ -170,6 +196,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 { @@ -202,11 +233,11 @@ func WithInactive(inactive bool) userSearchF { } } -// Find searches for user info from JIRA: +// FindWithContext searches for user info from JIRA: // It can find users by email, username or name // // 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) { +func (s *UserService) FindWithContext(ctx context.Context, property string, tweaks ...userSearchF) ([]User, *Response, error) { search := []userSearchParam{ { name: "username", @@ -223,7 +254,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 } @@ -235,3 +266,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...) +} diff --git a/version.go b/version.go index 28ebb75..567be78 100644 --- a/version.go +++ b/version.go @@ -1,6 +1,7 @@ package jira import ( + "context" "encoding/json" "fmt" "io/ioutil" @@ -27,12 +28,12 @@ type Version struct { 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) { +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) { +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 } @@ -75,12 +81,17 @@ func (s *VersionService) Create(version *Version) (*Version, *Response, error) { 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) { +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 +106,8 @@ func (s *VersionService) Update(version *Version) (*Version, *Response, error) { ret := *version return &ret, resp, nil } + +// Update wraps UpdateWithContext using the background context. +func (s *VersionService) Update(version *Version) (*Version, *Response, error) { + return s.UpdateWithContext(context.Background(), version) +}