Skip to content

Commit

Permalink
handle resource leaks (#399)
Browse files Browse the repository at this point in the history
Co-authored-by: Benji Vesterby <benji@devnw.com>
  • Loading branch information
dustin-decker and benjivesterby committed Feb 26, 2022
1 parent 13da37b commit 1e16435
Show file tree
Hide file tree
Showing 12 changed files with 63 additions and 10 deletions.
4 changes: 2 additions & 2 deletions authentication.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ func (s *AuthenticationService) LogoutWithContext(ctx context.Context) error {
if err != nil {
return fmt.Errorf("error sending the logout request: %s", err)
}
defer resp.Body.Close()
if resp.StatusCode != 204 {
return fmt.Errorf("the logout was unsuccessful with status %d", resp.StatusCode)
}
Expand Down Expand Up @@ -182,11 +183,10 @@ func (s *AuthenticationService) GetCurrentUserWithContext(ctx context.Context) (
if err != nil {
return nil, fmt.Errorf("error sending request to get user info : %s", err)
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return nil, fmt.Errorf("getting user info failed with status : %d", resp.StatusCode)
}

defer resp.Body.Close()
ret := new(Session)
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions board.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ func (s *BoardService) CreateBoard(board *Board) (*Board, *Response, error) {
// DeleteBoardWithContext will delete an agile board.
//
// Jira API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-deleteBoard
// Caller must close resp.Body
func (s *BoardService) DeleteBoardWithContext(ctx context.Context, boardID int) (*Board, *Response, error) {
apiEndpoint := fmt.Sprintf("rest/agile/1.0/board/%v", boardID)
req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, nil)
Expand All @@ -230,6 +231,7 @@ func (s *BoardService) DeleteBoardWithContext(ctx context.Context, boardID int)
}

// DeleteBoard wraps DeleteBoardWithContext using the background context.
// Caller must close resp.Body
func (s *BoardService) DeleteBoard(boardID int) (*Board, *Response, error) {
return s.DeleteBoardWithContext(context.Background(), boardID)
}
Expand Down
3 changes: 2 additions & 1 deletion examples/do/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ func main() {
req, _ := jiraClient.NewRequest("GET", "/rest/api/2/project", nil)

projects := new([]jira.Project)
_, err := jiraClient.Do(req, projects)
res, err := jiraClient.Do(req, projects)
if err != nil {
panic(err)
}
defer res.Body.Close()

for _, project := range *projects {
fmt.Printf("%s: %s\n", project.Key, project.Name)
Expand Down
2 changes: 2 additions & 0 deletions group.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ func (s *GroupService) Add(groupname string, username string) (*Group, *Response
// RemoveWithContext removes user from group
//
// Jira API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/group-removeUserFromGroup
// Caller must close resp.Body
func (s *GroupService) RemoveWithContext(ctx context.Context, groupname string, username string) (*Response, error) {
apiEndpoint := fmt.Sprintf("/rest/api/2/group/user?groupname=%s&username=%s", groupname, username)
req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, nil)
Expand All @@ -172,6 +173,7 @@ func (s *GroupService) RemoveWithContext(ctx context.Context, groupname string,
}

// Remove wraps RemoveWithContext using the background context.
// Caller must close resp.Body
func (s *GroupService) Remove(groupname string, username string) (*Response, error) {
return s.RemoveWithContext(context.Background(), groupname, username)
}
25 changes: 24 additions & 1 deletion issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -649,7 +649,7 @@ func (s *IssueService) Get(issueID string, options *GetQueryOptions) (*Issue, *R
// 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.
// Caller must close resp.Body.
func (s *IssueService) DownloadAttachmentWithContext(ctx context.Context, attachmentID string) (*Response, error) {
apiEndpoint := fmt.Sprintf("secure/attachment/%s/", attachmentID)
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
Expand All @@ -667,6 +667,7 @@ func (s *IssueService) DownloadAttachmentWithContext(ctx context.Context, attach
}

// DownloadAttachment wraps DownloadAttachmentWithContext using the background context.
// Caller must close resp.Body
func (s *IssueService) DownloadAttachment(attachmentID string) (*Response, error) {
return s.DownloadAttachmentWithContext(context.Background(), attachmentID)
}
Expand Down Expand Up @@ -715,6 +716,7 @@ func (s *IssueService) PostAttachment(issueID string, r io.Reader, attachmentNam
}

// DeleteAttachmentWithContext deletes an attachment of a given attachmentID
// Caller must close resp.Body
func (s *IssueService) DeleteAttachmentWithContext(ctx context.Context, attachmentID string) (*Response, error) {
apiEndpoint := fmt.Sprintf("rest/api/2/attachment/%s", attachmentID)

Expand All @@ -733,11 +735,13 @@ func (s *IssueService) DeleteAttachmentWithContext(ctx context.Context, attachme
}

// DeleteAttachment wraps DeleteAttachmentWithContext using the background context.
// Caller must close resp.Body
func (s *IssueService) DeleteAttachment(attachmentID string) (*Response, error) {
return s.DeleteAttachmentWithContext(context.Background(), attachmentID)
}

// DeleteLinkWithContext deletes a link of a given linkID
// Caller must close resp.Body
func (s *IssueService) DeleteLinkWithContext(ctx context.Context, linkID string) (*Response, error) {
apiEndpoint := fmt.Sprintf("rest/api/2/issueLink/%s", linkID)

Expand All @@ -756,6 +760,7 @@ func (s *IssueService) DeleteLinkWithContext(ctx context.Context, linkID string)
}

// DeleteLink wraps DeleteLinkWithContext using the background context.
// Caller must close resp.Body
func (s *IssueService) DeleteLink(linkID string) (*Response, error) {
return s.DeleteLinkWithContext(context.Background(), linkID)
}
Expand Down Expand Up @@ -844,6 +849,7 @@ func (s *IssueService) Create(issue *Issue) (*Issue, *Response, error) {
// while also specifying query params. The issue is found by key.
//
// Jira API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/issue-editIssue
// Caller must close resp.Body
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)
Expand All @@ -867,6 +873,7 @@ func (s *IssueService) UpdateWithOptionsWithContext(ctx context.Context, issue *
}

// UpdateWithOptions wraps UpdateWithOptionsWithContext using the background context.
// Caller must close resp.Body
func (s *IssueService) UpdateWithOptions(issue *Issue, opts *UpdateQueryOptions) (*Issue, *Response, error) {
return s.UpdateWithOptionsWithContext(context.Background(), issue, opts)
}
Expand All @@ -886,6 +893,7 @@ func (s *IssueService) Update(issue *Issue) (*Issue, *Response, error) {
// 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
// Caller must close resp.Body
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.NewRequestWithContext(ctx, "PUT", apiEndpoint, data)
Expand All @@ -903,6 +911,7 @@ func (s *IssueService) UpdateIssueWithContext(ctx context.Context, jiraID string
}

// UpdateIssue wraps UpdateIssueWithContext using the background context.
// Caller must close resp.Body
func (s *IssueService) UpdateIssue(jiraID string, data map[string]interface{}) (*Response, error) {
return s.UpdateIssueWithContext(context.Background(), jiraID, data)
}
Expand Down Expand Up @@ -976,6 +985,7 @@ func (s *IssueService) DeleteCommentWithContext(ctx context.Context, issueID, co
jerr := NewJiraError(resp, err)
return jerr
}
defer resp.Body.Close()

return nil
}
Expand Down Expand Up @@ -1052,6 +1062,7 @@ func (s *IssueService) UpdateWorklogRecord(issueID, worklogID string, record *Wo
// AddLinkWithContext adds a link between two issues.
//
// Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issueLink
// Caller must close resp.Body
func (s *IssueService) AddLinkWithContext(ctx context.Context, issueLink *IssueLink) (*Response, error) {
apiEndpoint := "rest/api/2/issueLink"
req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, issueLink)
Expand All @@ -1068,6 +1079,7 @@ func (s *IssueService) AddLinkWithContext(ctx context.Context, issueLink *IssueL
}

// AddLink wraps AddLinkWithContext using the background context.
// Caller must close resp.Body
func (s *IssueService) AddLink(issueLink *IssueLink) (*Response, error) {
return s.AddLinkWithContext(context.Background(), issueLink)
}
Expand Down Expand Up @@ -1259,6 +1271,7 @@ func (s *IssueService) DoTransition(ticketID, transitionID string) (*Response, e
// 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
// Caller must close resp.Body
func (s *IssueService) DoTransitionWithPayloadWithContext(ctx context.Context, ticketID, payload interface{}) (*Response, error) {
apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/transitions", ticketID)

Expand All @@ -1276,6 +1289,7 @@ func (s *IssueService) DoTransitionWithPayloadWithContext(ctx context.Context, t
}

// DoTransitionWithPayload wraps DoTransitionWithPayloadWithContext using the background context.
// Caller must close resp.Body
func (s *IssueService) DoTransitionWithPayload(ticketID, payload interface{}) (*Response, error) {
return s.DoTransitionWithPayloadWithContext(context.Background(), ticketID, payload)
}
Expand Down Expand Up @@ -1360,6 +1374,7 @@ func InitIssueWithMetaAndFields(metaProject *MetaProject, metaIssuetype *MetaIss
}

// DeleteWithContext will delete a specified issue.
// Caller must close resp.Body
func (s *IssueService) DeleteWithContext(ctx context.Context, issueID string) (*Response, error) {
apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s", issueID)

Expand All @@ -1378,6 +1393,7 @@ func (s *IssueService) DeleteWithContext(ctx context.Context, issueID string) (*
}

// Delete wraps DeleteWithContext using the background context.
// Caller must close resp.Body
func (s *IssueService) Delete(issueID string) (*Response, error) {
return s.DeleteWithContext(context.Background(), issueID)
}
Expand Down Expand Up @@ -1422,6 +1438,7 @@ func (s *IssueService) GetWatchers(issueID string) (*[]User, *Response, error) {
// AddWatcherWithContext adds watcher to the given issue
//
// Jira API docs: https://docs.atlassian.com/software/jira/docs/api/REST/latest/#api/2/issue-addWatcher
// Caller must close resp.Body
func (s *IssueService) AddWatcherWithContext(ctx context.Context, issueID string, userName string) (*Response, error) {
apiEndPoint := fmt.Sprintf("rest/api/2/issue/%s/watchers", issueID)

Expand All @@ -1439,13 +1456,15 @@ func (s *IssueService) AddWatcherWithContext(ctx context.Context, issueID string
}

// AddWatcher wraps AddWatcherWithContext using the background context.
// Caller must close resp.Body
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
// Caller must close resp.Body
func (s *IssueService) RemoveWatcherWithContext(ctx context.Context, issueID string, userName string) (*Response, error) {
apiEndPoint := fmt.Sprintf("rest/api/2/issue/%s/watchers", issueID)

Expand All @@ -1463,13 +1482,15 @@ func (s *IssueService) RemoveWatcherWithContext(ctx context.Context, issueID str
}

// RemoveWatcher wraps RemoveWatcherWithContext using the background context.
// Caller must close resp.Body
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
// Caller must close resp.Body
func (s *IssueService) UpdateAssigneeWithContext(ctx context.Context, issueID string, assignee *User) (*Response, error) {
apiEndPoint := fmt.Sprintf("rest/api/2/issue/%s/assignee", issueID)

Expand All @@ -1487,6 +1508,7 @@ func (s *IssueService) UpdateAssigneeWithContext(ctx context.Context, issueID st
}

// UpdateAssignee wraps UpdateAssigneeWithContext using the background context.
// Caller must close resp.Body
func (s *IssueService) UpdateAssignee(issueID string, assignee *User) (*Response, error) {
return s.UpdateAssigneeWithContext(context.Background(), issueID, assignee)
}
Expand Down Expand Up @@ -1520,6 +1542,7 @@ func (s *IssueService) GetRemoteLinksWithContext(ctx context.Context, id string)
}

// GetRemoteLinks wraps GetRemoteLinksWithContext using the background context.
// Caller must close resp.Body
func (s *IssueService) GetRemoteLinks(id string) (*[]RemoteLink, *Response, error) {
return s.GetRemoteLinksWithContext(context.Background(), id)
}
Expand Down
4 changes: 4 additions & 0 deletions issuelinktype.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ func (s *IssueLinkTypeService) Create(linkType *IssueLinkType) (*IssueLinkType,
// 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
// Caller must close resp.Body
func (s *IssueLinkTypeService) UpdateWithContext(ctx context.Context, linkType *IssueLinkType) (*IssueLinkType, *Response, error) {
apiEndpoint := fmt.Sprintf("rest/api/2/issueLinkType/%s", linkType.ID)
req, err := s.client.NewRequestWithContext(ctx, "PUT", apiEndpoint, linkType)
Expand All @@ -113,13 +114,15 @@ func (s *IssueLinkTypeService) UpdateWithContext(ctx context.Context, linkType *
}

// Update wraps UpdateWithContext using the background context.
// Caller must close resp.Body
func (s *IssueLinkTypeService) Update(linkType *IssueLinkType) (*IssueLinkType, *Response, error) {
return s.UpdateWithContext(context.Background(), linkType)
}

// DeleteWithContext deletes an issue link type based on provided ID.
//
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issueLinkType-issueLinkTypeId-delete
// Caller must close resp.Body
func (s *IssueLinkTypeService) DeleteWithContext(ctx context.Context, ID string) (*Response, error) {
apiEndpoint := fmt.Sprintf("rest/api/2/issueLinkType/%s", ID)
req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, nil)
Expand All @@ -132,6 +135,7 @@ func (s *IssueLinkTypeService) DeleteWithContext(ctx context.Context, ID string)
}

// Delete wraps DeleteWithContext using the background context.
// Caller must close resp.Body
func (s *IssueLinkTypeService) Delete(ID string) (*Response, error) {
return s.DeleteWithContext(context.Background(), ID)
}
1 change: 1 addition & 0 deletions jira.go
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,7 @@ func (t *CookieAuthTransport) setSessionObject() error {
if err != nil {
return err
}
defer resp.Body.Close()

t.SessionObject = resp.Cookies()
return nil
Expand Down
10 changes: 10 additions & 0 deletions organization.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ func (s *OrganizationService) GetOrganization(organizationID int) (*Organization
// For example, associations with service desks.
//
// Jira API docs: https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-organization/#api-rest-servicedeskapi-organization-organizationid-delete
// Caller must close resp.Body
func (s *OrganizationService) DeleteOrganizationWithContext(ctx context.Context, organizationID int) (*Response, error) {
apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d", organizationID)

Expand All @@ -180,6 +181,7 @@ func (s *OrganizationService) DeleteOrganizationWithContext(ctx context.Context,
}

// DeleteOrganization wraps DeleteOrganizationWithContext using the background context.
// Caller must close resp.Body
func (s *OrganizationService) DeleteOrganization(organizationID int) (*Response, error) {
return s.DeleteOrganizationWithContext(context.Background(), organizationID)
}
Expand Down Expand Up @@ -250,6 +252,7 @@ func (s *OrganizationService) GetProperty(organizationID int, propertyKey string
// resource to store custom data against an organization.
//
// https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-organization/#api-rest-servicedeskapi-organization-organizationid-property-propertykey-put
// Caller must close resp.Body
func (s *OrganizationService) SetPropertyWithContext(ctx context.Context, organizationID int, propertyKey string) (*Response, error) {
apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d/property/%s", organizationID, propertyKey)

Expand All @@ -270,13 +273,15 @@ func (s *OrganizationService) SetPropertyWithContext(ctx context.Context, organi
}

// SetProperty wraps SetPropertyWithContext using the background context.
// Caller must close resp.Body
func (s *OrganizationService) SetProperty(organizationID int, propertyKey string) (*Response, error) {
return s.SetPropertyWithContext(context.Background(), organizationID, propertyKey)
}

// DeletePropertyWithContext removes a property from an organization.
//
// https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-organization/#api-rest-servicedeskapi-organization-organizationid-property-propertykey-delete
// Caller must close resp.Body
func (s *OrganizationService) DeletePropertyWithContext(ctx context.Context, organizationID int, propertyKey string) (*Response, error) {
apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d/property/%s", organizationID, propertyKey)

Expand All @@ -297,6 +302,7 @@ func (s *OrganizationService) DeletePropertyWithContext(ctx context.Context, org
}

// DeleteProperty wraps DeletePropertyWithContext using the background context.
// Caller must close resp.Body
func (s *OrganizationService) DeleteProperty(organizationID int, propertyKey string) (*Response, error) {
return s.DeletePropertyWithContext(context.Background(), organizationID, propertyKey)
}
Expand Down Expand Up @@ -336,6 +342,7 @@ func (s *OrganizationService) GetUsers(organizationID int, start int, limit int)
// AddUsersWithContext adds users to an organization.
//
// https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-organization/#api-rest-servicedeskapi-organization-organizationid-user-post
// Caller must close resp.Body
func (s *OrganizationService) AddUsersWithContext(ctx context.Context, organizationID int, users OrganizationUsersDTO) (*Response, error) {
apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d/user", organizationID)

Expand All @@ -355,13 +362,15 @@ func (s *OrganizationService) AddUsersWithContext(ctx context.Context, organizat
}

// AddUsers wraps AddUsersWithContext using the background context.
// Caller must close resp.Body
func (s *OrganizationService) AddUsers(organizationID int, users OrganizationUsersDTO) (*Response, error) {
return s.AddUsersWithContext(context.Background(), organizationID, users)
}

// RemoveUsersWithContext removes users from an organization.
//
// https://developer.atlassian.com/cloud/jira/service-desk/rest/api-group-organization/#api-rest-servicedeskapi-organization-organizationid-user-delete
// Caller must close resp.Body
func (s *OrganizationService) RemoveUsersWithContext(ctx context.Context, organizationID int, users OrganizationUsersDTO) (*Response, error) {
apiEndPoint := fmt.Sprintf("rest/servicedeskapi/organization/%d/user", organizationID)

Expand All @@ -382,6 +391,7 @@ func (s *OrganizationService) RemoveUsersWithContext(ctx context.Context, organi
}

// RemoveUsers wraps RemoveUsersWithContext using the background context.
// Caller must close resp.Body
func (s *OrganizationService) RemoveUsers(organizationID int, users OrganizationUsersDTO) (*Response, error) {
return s.RemoveUsersWithContext(context.Background(), organizationID, users)
}

0 comments on commit 1e16435

Please sign in to comment.