mirror of
https://github.com/Threnklyn/jira.git
synced 2026-05-18 20:23:28 +02:00
579 lines
16 KiB
Go
579 lines
16 KiB
Go
package jira
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"mime/multipart"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"github.com/coryb/oreo"
|
|
|
|
"gopkg.in/Netflix-Skunkworks/go-jira.v1/jiradata"
|
|
)
|
|
|
|
type IssueQueryProvider interface {
|
|
ProvideIssueQueryString() string
|
|
}
|
|
|
|
type IssueOptions struct {
|
|
Fields []string `json:"fields,omitempty" yaml:"fields,omitempty"`
|
|
Expand []string `json:"expand,omitempty" yaml:"expand,omitempty"`
|
|
Properties []string `json:"properties,omitempty" yaml:"properties,omitempty"`
|
|
FieldsByKeys bool `json:"fieldsByKeys,omitempty" yaml:"fieldsByKeys,omitempty"`
|
|
UpdateHistory bool `json:"updateHistory,omitempty" yaml:"updateHistory,omitempty"`
|
|
}
|
|
|
|
func (o *IssueOptions) ProvideIssueQueryString() string {
|
|
params := []string{}
|
|
if len(o.Fields) > 0 {
|
|
params = append(params, "fields="+strings.Join(o.Fields, ","))
|
|
}
|
|
if len(o.Expand) > 0 {
|
|
params = append(params, "expand="+strings.Join(o.Expand, ","))
|
|
}
|
|
if len(o.Properties) > 0 {
|
|
params = append(params, "properties="+strings.Join(o.Properties, ","))
|
|
}
|
|
if o.FieldsByKeys {
|
|
params = append(params, "fieldsByKeys=true")
|
|
}
|
|
if o.UpdateHistory {
|
|
params = append(params, "updateHistory=true")
|
|
}
|
|
if len(params) > 0 {
|
|
return "?" + strings.Join(params, "&")
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// https://docs.atlassian.com/jira/REST/cloud/#api/2/issue-getIssue
|
|
func (j *Jira) GetIssue(issue string, iqg IssueQueryProvider) (*jiradata.Issue, error) {
|
|
return GetIssue(j.UA, j.Endpoint, issue, iqg)
|
|
}
|
|
|
|
func GetIssue(ua HttpClient, endpoint string, issue string, iqg IssueQueryProvider) (*jiradata.Issue, error) {
|
|
query := ""
|
|
if iqg != nil {
|
|
query = iqg.ProvideIssueQueryString()
|
|
}
|
|
uri := fmt.Sprintf("%s/rest/api/2/issue/%s%s", endpoint, issue, query)
|
|
resp, err := ua.GetJSON(uri)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode == 200 {
|
|
results := &jiradata.Issue{}
|
|
return results, readJSON(resp.Body, results)
|
|
}
|
|
return nil, responseError(resp)
|
|
}
|
|
|
|
func (j *Jira) GetIssueWorklog(issue string) (*jiradata.Worklogs, error) {
|
|
return GetIssueWorklog(j.UA, j.Endpoint, issue)
|
|
}
|
|
|
|
// https://docs.atlassian.com/jira/REST/cloud/#api/2/issue/{issueIdOrKey}/worklog-getIssueWorklog
|
|
func GetIssueWorklog(ua HttpClient, endpoint string, issue string) (*jiradata.Worklogs, error) {
|
|
startAt := 0
|
|
total := 1
|
|
maxResults := 100
|
|
worklogs := jiradata.Worklogs{}
|
|
for startAt < total {
|
|
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/worklog?startAt=%d&maxResults=%d", endpoint, issue, startAt, maxResults)
|
|
resp, err := ua.GetJSON(uri)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode == 200 {
|
|
results := &jiradata.WorklogWithPagination{}
|
|
err := readJSON(resp.Body, results)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
startAt = startAt + maxResults
|
|
total = results.Total
|
|
for _, worklog := range results.Worklogs {
|
|
worklogs = append(worklogs, worklog)
|
|
}
|
|
} else {
|
|
return nil, responseError(resp)
|
|
}
|
|
}
|
|
return &worklogs, nil
|
|
}
|
|
|
|
type WorklogProvider interface {
|
|
ProvideWorklog() *jiradata.Worklog
|
|
}
|
|
|
|
// https://docs.atlassian.com/jira/REST/cloud/#api/2/issue/{issueIdOrKey}/worklog-addWorklog
|
|
func (j *Jira) AddIssueWorklog(issue string, wp WorklogProvider) (*jiradata.Worklog, error) {
|
|
return AddIssueWorklog(j.UA, j.Endpoint, issue, wp)
|
|
}
|
|
|
|
func AddIssueWorklog(ua HttpClient, endpoint string, issue string, wp WorklogProvider) (*jiradata.Worklog, error) {
|
|
req := wp.ProvideWorklog()
|
|
encoded, err := json.Marshal(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/worklog", endpoint, issue)
|
|
resp, err := ua.Post(uri, "application/json", bytes.NewBuffer(encoded))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode == 201 {
|
|
results := &jiradata.Worklog{}
|
|
return results, readJSON(resp.Body, results)
|
|
}
|
|
return nil, responseError(resp)
|
|
}
|
|
|
|
// https://docs.atlassian.com/jira/REST/cloud/#api/2/issue-getEditIssueMeta
|
|
func (j *Jira) GetIssueEditMeta(issue string) (*jiradata.EditMeta, error) {
|
|
return GetIssueEditMeta(j.UA, j.Endpoint, issue)
|
|
}
|
|
|
|
func GetIssueEditMeta(ua HttpClient, endpoint string, issue string) (*jiradata.EditMeta, error) {
|
|
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/editmeta", endpoint, issue)
|
|
resp, err := ua.GetJSON(uri)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode == 200 {
|
|
results := &jiradata.EditMeta{}
|
|
return results, readJSON(resp.Body, results)
|
|
}
|
|
return nil, responseError(resp)
|
|
}
|
|
|
|
type IssueUpdateProvider interface {
|
|
ProvideIssueUpdate() *jiradata.IssueUpdate
|
|
}
|
|
|
|
// https://docs.atlassian.com/jira/REST/cloud/#api/2/issue-editIssue
|
|
func (j *Jira) EditIssue(issue string, iup IssueUpdateProvider) error {
|
|
return EditIssue(j.UA, j.Endpoint, issue, iup)
|
|
}
|
|
|
|
func EditIssue(ua HttpClient, endpoint string, issue string, iup IssueUpdateProvider) error {
|
|
req := iup.ProvideIssueUpdate()
|
|
encoded, err := json.Marshal(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
uri := fmt.Sprintf("%s/rest/api/2/issue/%s", endpoint, issue)
|
|
resp, err := ua.Put(uri, "application/json", bytes.NewBuffer(encoded))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode == 204 {
|
|
return nil
|
|
}
|
|
return responseError(resp)
|
|
}
|
|
|
|
// https://docs.atlassian.com/jira/REST/cloud/#api/2/issue-createIssue
|
|
func (j *Jira) CreateIssue(iup IssueUpdateProvider) (*jiradata.IssueCreateResponse, error) {
|
|
return CreateIssue(j.UA, j.Endpoint, iup)
|
|
}
|
|
|
|
func CreateIssue(ua HttpClient, endpoint string, iup IssueUpdateProvider) (*jiradata.IssueCreateResponse, error) {
|
|
req := iup.ProvideIssueUpdate()
|
|
encoded, err := json.Marshal(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
uri := fmt.Sprintf("%s/rest/api/2/issue", endpoint)
|
|
resp, err := ua.Post(uri, "application/json", bytes.NewBuffer(encoded))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode == 201 {
|
|
results := &jiradata.IssueCreateResponse{}
|
|
return results, readJSON(resp.Body, results)
|
|
}
|
|
return nil, responseError(resp)
|
|
}
|
|
|
|
// https://docs.atlassian.com/jira/REST/cloud/#api/2/issue-getCreateIssueMeta
|
|
func (j *Jira) GetIssueCreateMetaProject(projectKey string) (*jiradata.CreateMetaProject, error) {
|
|
return GetIssueCreateMetaProject(j.UA, j.Endpoint, projectKey)
|
|
}
|
|
|
|
func GetIssueCreateMetaProject(ua HttpClient, endpoint string, projectKey string) (*jiradata.CreateMetaProject, error) {
|
|
uri := fmt.Sprintf("%s/rest/api/2/issue/createmeta?projectKeys=%s&expand=projects.issuetypes.fields", endpoint, projectKey)
|
|
resp, err := ua.GetJSON(uri)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode == 200 {
|
|
results := &jiradata.CreateMeta{}
|
|
err = readJSON(resp.Body, results)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, project := range results.Projects {
|
|
if project.Key == projectKey {
|
|
return project, nil
|
|
}
|
|
}
|
|
return nil, fmt.Errorf("Project %s not found", projectKey)
|
|
}
|
|
return nil, responseError(resp)
|
|
}
|
|
|
|
// https://docs.atlassian.com/jira/REST/cloud/#api/2/issue-getCreateIssueMeta
|
|
func (j *Jira) GetIssueCreateMetaIssueType(projectKey, issueTypeName string) (*jiradata.IssueType, error) {
|
|
return GetIssueCreateMetaIssueType(j.UA, j.Endpoint, projectKey, issueTypeName)
|
|
}
|
|
|
|
func GetIssueCreateMetaIssueType(ua HttpClient, endpoint string, projectKey, issueTypeName string) (*jiradata.IssueType, error) {
|
|
uri := fmt.Sprintf("%s/rest/api/2/issue/createmeta?projectKeys=%s&issuetypeNames=%s&expand=projects.issuetypes.fields", endpoint, projectKey, url.QueryEscape(issueTypeName))
|
|
resp, err := ua.GetJSON(uri)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode == 200 {
|
|
results := &jiradata.CreateMeta{}
|
|
err = readJSON(resp.Body, results)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, project := range results.Projects {
|
|
if project.Key == projectKey {
|
|
for _, issueType := range project.IssueTypes {
|
|
if issueType.Name == issueTypeName {
|
|
return issueType, nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil, fmt.Errorf("Project %s and IssueType %s not found", projectKey, issueTypeName)
|
|
}
|
|
return nil, responseError(resp)
|
|
}
|
|
|
|
type LinkIssueProvider interface {
|
|
ProvideLinkIssueRequest() *jiradata.LinkIssueRequest
|
|
}
|
|
|
|
// https://docs.atlassian.com/jira/REST/cloud/#api/2/issueLink-linkIssues
|
|
func (j *Jira) LinkIssues(lip LinkIssueProvider) error {
|
|
return LinkIssues(j.UA, j.Endpoint, lip)
|
|
}
|
|
|
|
func LinkIssues(ua HttpClient, endpoint string, lip LinkIssueProvider) error {
|
|
req := lip.ProvideLinkIssueRequest()
|
|
encoded, err := json.Marshal(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
uri := fmt.Sprintf("%s/rest/api/2/issueLink", endpoint)
|
|
resp, err := ua.Post(uri, "application/json", bytes.NewBuffer(encoded))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode == 201 {
|
|
return nil
|
|
}
|
|
return responseError(resp)
|
|
}
|
|
|
|
// https://docs.atlassian.com/jira/REST/cloud/#api/2/issue-getTransitions
|
|
func (j *Jira) GetIssueTransitions(issue string) (*jiradata.TransitionsMeta, error) {
|
|
return GetIssueTransitions(j.UA, j.Endpoint, issue)
|
|
}
|
|
|
|
func GetIssueTransitions(ua HttpClient, endpoint string, issue string) (*jiradata.TransitionsMeta, error) {
|
|
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/transitions?expand=transitions.fields", endpoint, issue)
|
|
resp, err := ua.GetJSON(uri)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode == 200 {
|
|
results := &jiradata.TransitionsMeta{}
|
|
return results, readJSON(resp.Body, results)
|
|
}
|
|
return nil, responseError(resp)
|
|
}
|
|
|
|
// https://docs.atlassian.com/jira/REST/cloud/#api/2/issue-doTransition
|
|
func (j *Jira) TransitionIssue(issue string, iup IssueUpdateProvider) error {
|
|
return TransitionIssue(j.UA, j.Endpoint, issue, iup)
|
|
}
|
|
|
|
func TransitionIssue(ua HttpClient, endpoint string, issue string, iup IssueUpdateProvider) error {
|
|
req := iup.ProvideIssueUpdate()
|
|
encoded, err := json.Marshal(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/transitions", endpoint, issue)
|
|
resp, err := ua.Post(uri, "application/json", bytes.NewBuffer(encoded))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode == 204 {
|
|
return nil
|
|
}
|
|
return responseError(resp)
|
|
}
|
|
|
|
// https://docs.atlassian.com/jira/REST/cloud/#api/2/issueLinkType-getIssueLinkTypes
|
|
func (j *Jira) GetIssueLinkTypes() (*jiradata.IssueLinkTypes, error) {
|
|
return GetIssueLinkTypes(j.UA, j.Endpoint)
|
|
}
|
|
|
|
func GetIssueLinkTypes(ua HttpClient, endpoint string) (*jiradata.IssueLinkTypes, error) {
|
|
uri := fmt.Sprintf("%s/rest/api/2/issueLinkType", endpoint)
|
|
resp, err := ua.GetJSON(uri)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode == 200 {
|
|
results := struct {
|
|
IssueLinkTypes jiradata.IssueLinkTypes
|
|
}{
|
|
IssueLinkTypes: jiradata.IssueLinkTypes{},
|
|
}
|
|
return &results.IssueLinkTypes, readJSON(resp.Body, &results)
|
|
}
|
|
return nil, responseError(resp)
|
|
}
|
|
|
|
// https://docs.atlassian.com/jira/REST/cloud/#api/2/issue-addVote
|
|
func (j *Jira) IssueAddVote(issue string) error {
|
|
return IssueAddVote(j.UA, j.Endpoint, issue)
|
|
}
|
|
|
|
func IssueAddVote(ua HttpClient, endpoint string, issue string) error {
|
|
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/votes", endpoint, issue)
|
|
resp, err := ua.Post(uri, "application/json", strings.NewReader("{}"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode == 204 {
|
|
return nil
|
|
}
|
|
return responseError(resp)
|
|
}
|
|
|
|
// https://docs.atlassian.com/jira/REST/cloud/#api/2/issue-removeVote
|
|
func (j *Jira) IssueRemoveVote(issue string) error {
|
|
return IssueRemoveVote(j.UA, j.Endpoint, issue)
|
|
}
|
|
|
|
func IssueRemoveVote(ua HttpClient, endpoint string, issue string) error {
|
|
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/votes", endpoint, issue)
|
|
resp, err := ua.Delete(uri)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode == 204 {
|
|
return nil
|
|
}
|
|
return responseError(resp)
|
|
}
|
|
|
|
type RankRequestProvider interface {
|
|
ProvideRankRequest() *jiradata.RankRequest
|
|
}
|
|
|
|
// https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/issue-rankIssues
|
|
func (j *Jira) RankIssues(rrp RankRequestProvider) error {
|
|
return RankIssues(j.UA, j.Endpoint, rrp)
|
|
}
|
|
|
|
func RankIssues(ua HttpClient, endpoint string, rrp RankRequestProvider) error {
|
|
req := rrp.ProvideRankRequest()
|
|
encoded, err := json.Marshal(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
uri := fmt.Sprintf("%s/rest/agile/1.0/issue/rank", endpoint)
|
|
resp, err := ua.Put(uri, "application/json", bytes.NewBuffer(encoded))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode == 204 {
|
|
return nil
|
|
}
|
|
return responseError(resp)
|
|
}
|
|
|
|
// https://docs.atlassian.com/jira/REST/cloud/#api/2/issue-addWatcher
|
|
func (j *Jira) IssueAddWatcher(issue, user string) error {
|
|
return IssueAddWatcher(j.UA, j.Endpoint, issue, user)
|
|
}
|
|
|
|
func IssueAddWatcher(ua HttpClient, endpoint string, issue, user string) error {
|
|
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/watchers", endpoint, issue)
|
|
resp, err := ua.Post(uri, "application/json", strings.NewReader(fmt.Sprintf("%q", user)))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode == 204 {
|
|
return nil
|
|
}
|
|
return responseError(resp)
|
|
}
|
|
|
|
// https://docs.atlassian.com/jira/REST/cloud/#api/2/issue-addWatcher
|
|
func (j *Jira) IssueRemoveWatcher(issue, user string) error {
|
|
return IssueRemoveWatcher(j.UA, j.Endpoint, issue, user)
|
|
}
|
|
|
|
func IssueRemoveWatcher(ua HttpClient, endpoint string, issue, user string) error {
|
|
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/watchers?username=%s", endpoint, issue, user)
|
|
resp, err := ua.Delete(uri)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode == 204 {
|
|
return nil
|
|
}
|
|
return responseError(resp)
|
|
}
|
|
|
|
type CommentProvider interface {
|
|
ProvideComment() *jiradata.Comment
|
|
}
|
|
|
|
// https://docs.atlassian.com/jira/REST/cloud/#api/2/issue/{issueIdOrKey}/comment-addComment
|
|
func (j *Jira) IssueAddComment(issue string, cp CommentProvider) (*jiradata.Comment, error) {
|
|
return IssueAddComment(j.UA, j.Endpoint, issue, cp)
|
|
}
|
|
|
|
func IssueAddComment(ua HttpClient, endpoint string, issue string, cp CommentProvider) (*jiradata.Comment, error) {
|
|
req := cp.ProvideComment()
|
|
encoded, err := json.Marshal(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/comment", endpoint, issue)
|
|
resp, err := ua.Post(uri, "application/json", bytes.NewBuffer(encoded))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode == 201 {
|
|
results := jiradata.Comment{}
|
|
return &results, readJSON(resp.Body, &results)
|
|
}
|
|
return nil, responseError(resp)
|
|
}
|
|
|
|
type UserProvider interface {
|
|
ProvideUser() *jiradata.User
|
|
}
|
|
|
|
// https://docs.atlassian.com/jira/REST/cloud/#api/2/issue-assign
|
|
func (j *Jira) IssueAssign(issue, name string) error {
|
|
return IssueAssign(j.UA, j.Endpoint, issue, name)
|
|
}
|
|
|
|
func IssueAssign(ua HttpClient, endpoint string, issue, name string) error {
|
|
// this is special, not using the jiradata.User structure
|
|
// because we need to be able to send `null` as the name param
|
|
// when we want to un-assign the issue
|
|
req := struct {
|
|
Name *string `json:"name"`
|
|
}{&name}
|
|
if name == "" {
|
|
req.Name = nil
|
|
}
|
|
|
|
encoded, err := json.Marshal(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
uri := fmt.Sprintf("%s/rest/api/2/issue/%s/assignee", endpoint, issue)
|
|
resp, err := ua.Put(uri, "application/json", bytes.NewBuffer(encoded))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode == 204 {
|
|
return nil
|
|
}
|
|
return responseError(resp)
|
|
}
|
|
|
|
// https://docs.atlassian.com/jira/REST/cloud/#api/2/issue/{issueIdOrKey}/attachments-addAttachment
|
|
func (j *Jira) IssueAttachFile(issue, filename string, contents io.Reader) (*jiradata.ListOfAttachment, error) {
|
|
return IssueAttachFile(j.UA, j.Endpoint, issue, filename, contents)
|
|
}
|
|
|
|
func IssueAttachFile(ua HttpClient, endpoint string, issue, filename string, contents io.Reader) (*jiradata.ListOfAttachment, error) {
|
|
var buf bytes.Buffer
|
|
w := multipart.NewWriter(&buf)
|
|
formFile, err := w.CreateFormFile("file", filename)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
_, err = io.Copy(formFile, contents)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
uri, err := url.Parse(fmt.Sprintf("%s/rest/api/2/issue/%s/attachments", endpoint, issue))
|
|
req := oreo.RequestBuilder(uri).WithMethod("POST").WithHeader(
|
|
"X-Atlassian-Token", "no-check",
|
|
).WithHeader(
|
|
"Accept", "application/json",
|
|
).WithContentType(w.FormDataContentType()).WithBody(&buf).Build()
|
|
w.Close()
|
|
|
|
resp, err := ua.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode == 200 {
|
|
results := jiradata.ListOfAttachment{}
|
|
return &results, readJSON(resp.Body, &results)
|
|
}
|
|
return nil, responseError(resp)
|
|
}
|