2015-09-03 12:25:21 +02:00
package jira
import (
2016-05-19 23:11:21 +02:00
"bytes"
2020-05-03 15:38:32 +02:00
"context"
2016-09-23 16:19:07 +02:00
"encoding/json"
2015-09-03 12:25:21 +02:00
"fmt"
2016-05-19 23:11:21 +02:00
"io"
2016-10-05 14:53:30 +02:00
"io/ioutil"
2016-05-19 23:11:21 +02:00
"mime/multipart"
2019-05-05 02:22:09 +02:00
"net/http"
2016-05-29 17:30:45 +02:00
"net/url"
2016-09-23 16:19:07 +02:00
"reflect"
2020-05-02 23:14:19 +02:00
"strconv"
2016-05-29 17:30:45 +02:00
"strings"
"time"
2016-10-03 13:33:46 +02:00
"github.com/fatih/structs"
2017-01-28 00:04:08 +02:00
"github.com/google/go-querystring/query"
2016-10-03 13:33:46 +02:00
"github.com/trivago/tgo/tcontainer"
2015-09-03 12:25:21 +02:00
)
const (
2020-05-14 17:18:31 +02:00
// AssigneeAutomatic represents the value of the "Assignee: Automatic" of Jira
2015-09-03 12:25:21 +02:00
AssigneeAutomatic = "-1"
)
2020-05-14 17:18:31 +02:00
// IssueService handles Issues for the Jira instance / API.
2015-09-03 12:25:21 +02:00
//
2020-05-14 17:18:31 +02:00
// Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue
2015-09-03 12:25:21 +02:00
type IssueService struct {
client * Client
}
2018-10-17 21:44:13 +02:00
// UpdateQueryOptions specifies the optional parameters to the Edit issue
type UpdateQueryOptions struct {
2018-10-17 22:25:06 +02:00
NotifyUsers bool ` url:"notifyUsers,omitempty" `
OverrideScreenSecurity bool ` url:"overrideScreenSecurity,omitempty" `
OverrideEditableFlag bool ` url:"overrideEditableFlag,omitempty" `
2015-09-03 12:25:21 +02:00
}
2020-05-14 17:18:31 +02:00
// Issue represents a Jira issue.
2015-09-03 12:25:21 +02:00
type Issue struct {
2018-06-03 23:42:41 +02:00
Expand string ` json:"expand,omitempty" structs:"expand,omitempty" `
ID string ` json:"id,omitempty" structs:"id,omitempty" `
Self string ` json:"self,omitempty" structs:"self,omitempty" `
Key string ` json:"key,omitempty" structs:"key,omitempty" `
Fields * IssueFields ` json:"fields,omitempty" structs:"fields,omitempty" `
RenderedFields * IssueRenderedFields ` json:"renderedFields,omitempty" structs:"renderedFields,omitempty" `
Changelog * Changelog ` json:"changelog,omitempty" structs:"changelog,omitempty" `
2019-12-02 17:15:40 +02:00
Transitions [ ] Transition ` json:"transitions,omitempty" structs:"transitions,omitempty" `
2020-05-02 10:43:01 +02:00
Names map [ string ] string ` json:"names,omitempty" structs:"names,omitempty" `
2017-01-28 00:04:08 +02:00
}
// ChangelogItems reflects one single changelog item of a history item
type ChangelogItems struct {
Field string ` json:"field" structs:"field" `
FieldType string ` json:"fieldtype" structs:"fieldtype" `
From interface { } ` json:"from" structs:"from" `
FromString string ` json:"fromString" structs:"fromString" `
To interface { } ` json:"to" structs:"to" `
ToString string ` json:"toString" structs:"toString" `
}
// ChangelogHistory reflects one single changelog history entry
type ChangelogHistory struct {
Id string ` json:"id" structs:"id" `
Author User ` json:"author" structs:"author" `
Created string ` json:"created" structs:"created" `
Items [ ] ChangelogItems ` json:"items" structs:"items" `
}
// Changelog reflects the change log of an issue
type Changelog struct {
Histories [ ] ChangelogHistory ` json:"histories,omitempty" `
2015-09-03 12:25:21 +02:00
}
2020-05-14 17:18:31 +02:00
// Attachment represents a Jira attachment
2016-05-19 23:11:21 +02:00
type Attachment struct {
2016-09-23 16:19:07 +02:00
Self string ` json:"self,omitempty" structs:"self,omitempty" `
ID string ` json:"id,omitempty" structs:"id,omitempty" `
Filename string ` json:"filename,omitempty" structs:"filename,omitempty" `
Author * User ` json:"author,omitempty" structs:"author,omitempty" `
Created string ` json:"created,omitempty" structs:"created,omitempty" `
Size int ` json:"size,omitempty" structs:"size,omitempty" `
MimeType string ` json:"mimeType,omitempty" structs:"mimeType,omitempty" `
Content string ` json:"content,omitempty" structs:"content,omitempty" `
Thumbnail string ` json:"thumbnail,omitempty" structs:"thumbnail,omitempty" `
2016-05-19 23:11:21 +02:00
}
2016-05-29 17:30:45 +02:00
2016-07-28 22:06:52 +02:00
// Epic represents the epic to which an issue is associated
// Not that this struct does not process the returned "color" value
type Epic struct {
2016-09-23 16:19:07 +02:00
ID int ` json:"id" structs:"id" `
Key string ` json:"key" structs:"key" `
Self string ` json:"self" structs:"self" `
Name string ` json:"name" structs:"name" `
Summary string ` json:"summary" structs:"summary" `
Done bool ` json:"done" structs:"done" `
2016-07-28 22:06:52 +02:00
}
2020-05-14 17:18:31 +02:00
// IssueFields represents single fields of a Jira issue.
// Every Jira issue has several fields attached.
2015-09-03 12:25:21 +02:00
type IssueFields struct {
// TODO Missing fields
2016-10-24 11:06:37 +02:00
// * "workratio": -1,
// * "lastViewed": null,
// * "environment": null,
2019-04-28 19:52:48 +02:00
Expand string ` json:"expand,omitempty" structs:"expand,omitempty" `
Type IssueType ` json:"issuetype,omitempty" structs:"issuetype,omitempty" `
Project Project ` json:"project,omitempty" structs:"project,omitempty" `
Resolution * Resolution ` json:"resolution,omitempty" structs:"resolution,omitempty" `
Priority * Priority ` json:"priority,omitempty" structs:"priority,omitempty" `
Resolutiondate Time ` json:"resolutiondate,omitempty" structs:"resolutiondate,omitempty" `
Created Time ` json:"created,omitempty" structs:"created,omitempty" `
Duedate Date ` json:"duedate,omitempty" structs:"duedate,omitempty" `
Watches * Watches ` json:"watches,omitempty" structs:"watches,omitempty" `
Assignee * User ` json:"assignee,omitempty" structs:"assignee,omitempty" `
Updated Time ` json:"updated,omitempty" structs:"updated,omitempty" `
Description string ` json:"description,omitempty" structs:"description,omitempty" `
Summary string ` json:"summary,omitempty" structs:"summary,omitempty" `
Creator * User ` json:"Creator,omitempty" structs:"Creator,omitempty" `
Reporter * User ` json:"reporter,omitempty" structs:"reporter,omitempty" `
Components [ ] * Component ` json:"components,omitempty" structs:"components,omitempty" `
Status * Status ` json:"status,omitempty" structs:"status,omitempty" `
Progress * Progress ` json:"progress,omitempty" structs:"progress,omitempty" `
AggregateProgress * Progress ` json:"aggregateprogress,omitempty" structs:"aggregateprogress,omitempty" `
TimeTracking * TimeTracking ` json:"timetracking,omitempty" structs:"timetracking,omitempty" `
TimeSpent int ` json:"timespent,omitempty" structs:"timespent,omitempty" `
TimeEstimate int ` json:"timeestimate,omitempty" structs:"timeestimate,omitempty" `
TimeOriginalEstimate int ` json:"timeoriginalestimate,omitempty" structs:"timeoriginalestimate,omitempty" `
Worklog * Worklog ` json:"worklog,omitempty" structs:"worklog,omitempty" `
IssueLinks [ ] * IssueLink ` json:"issuelinks,omitempty" structs:"issuelinks,omitempty" `
Comments * Comments ` json:"comment,omitempty" structs:"comment,omitempty" `
FixVersions [ ] * FixVersion ` json:"fixVersions,omitempty" structs:"fixVersions,omitempty" `
AffectsVersions [ ] * AffectsVersion ` json:"versions,omitempty" structs:"versions,omitempty" `
Labels [ ] string ` json:"labels,omitempty" structs:"labels,omitempty" `
Subtasks [ ] * Subtasks ` json:"subtasks,omitempty" structs:"subtasks,omitempty" `
Attachments [ ] * Attachment ` json:"attachment,omitempty" structs:"attachment,omitempty" `
Epic * Epic ` json:"epic,omitempty" structs:"epic,omitempty" `
Sprint * Sprint ` json:"sprint,omitempty" structs:"sprint,omitempty" `
Parent * Parent ` json:"parent,omitempty" structs:"parent,omitempty" `
AggregateTimeOriginalEstimate int ` json:"aggregatetimeoriginalestimate,omitempty" structs:"aggregatetimeoriginalestimate,omitempty" `
AggregateTimeSpent int ` json:"aggregatetimespent,omitempty" structs:"aggregatetimespent,omitempty" `
AggregateTimeEstimate int ` json:"aggregatetimeestimate,omitempty" structs:"aggregatetimeestimate,omitempty" `
2018-07-20 14:20:54 +02:00
Unknowns tcontainer . MarshalMap
2016-09-23 16:19:07 +02:00
}
2016-10-03 13:33:46 +02:00
// MarshalJSON is a custom JSON marshal function for the IssueFields structs.
2020-05-14 17:18:31 +02:00
// It handles Jira custom fields and maps those from / to "Unknowns" key.
2016-09-23 16:19:07 +02:00
func ( i * IssueFields ) MarshalJSON ( ) ( [ ] byte , error ) {
m := structs . Map ( i )
unknowns , okay := m [ "Unknowns" ]
if okay {
2017-05-01 15:03:03 +02:00
// if unknowns present, shift all key value from unknown to a level up
2016-09-23 16:19:07 +02:00
for key , value := range unknowns . ( tcontainer . MarshalMap ) {
m [ key ] = value
}
delete ( m , "Unknowns" )
}
return json . Marshal ( m )
}
2016-10-03 13:33:46 +02:00
// UnmarshalJSON is a custom JSON marshal function for the IssueFields structs.
2020-05-14 17:18:31 +02:00
// It handles Jira custom fields and maps those from / to "Unknowns" key.
2016-09-23 16:19:07 +02:00
func ( i * IssueFields ) UnmarshalJSON ( data [ ] byte ) error {
// Do the normal unmarshalling first
// Details for this way: http://choly.ca/post/go-json-marshalling/
type Alias IssueFields
aux := & struct {
* Alias
} {
Alias : ( * Alias ) ( i ) ,
}
if err := json . Unmarshal ( data , & aux ) ; err != nil {
return err
}
totalMap := tcontainer . NewMarshalMap ( )
err := json . Unmarshal ( data , & totalMap )
if err != nil {
return err
}
t := reflect . TypeOf ( * i )
for i := 0 ; i < t . NumField ( ) ; i ++ {
field := t . Field ( i )
tagDetail := field . Tag . Get ( "json" )
if tagDetail == "" {
// ignore if there are no tags
continue
}
options := strings . Split ( tagDetail , "," )
if len ( options ) == 0 {
style: Fix staticcheck (static analysis) errors for this library (#283)
* style: Fix staticcheck errors for "error strings should not be capitalized (ST1005)"
staticcheck is a static analysis tool for go.
It reports several "error strings should not be capitalized (ST1005)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "printf-style function with dynamic format ... (SA1006)"
staticcheck is a static analysis tool for go.
It reports several "printf-style function with dynamic format string and no further arguments should use print-style function instead (SA1006)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "type X is unused (U1000)"
staticcheck is a static analysis tool for go.
It reports several "type X is unused (U1000)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "should use X instead (S1003 & SA6005)"
staticcheck is a static analysis tool for go.
It reports several
- should use !bytes.Contains(b, []byte(`"password":"bar"`)) instead (S1003)
- should use strings.EqualFold instead (SA6005)
messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "unnecessary use of fmt.Sprintf (S1039)"
staticcheck is a static analysis tool for go.
It report several "unnecessary use of fmt.Sprintf (S1039)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "this value of X is never used (SA4006)"
staticcheck is a static analysis tool for go.
It report several "this value of X is never used (SA4006)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "redundant return statement (S1023)"
staticcheck is a static analysis tool for go.
It report several "redundant return statement (S1023)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "possible nil pointer dereference (SA5011)"
staticcheck is a static analysis tool for go.
It report several
file.go:Line:character: possible nil pointer dereference (SA5011)
file.go:Line:character: this check suggests that the pointer can be nil
messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "this value of X is never used (SA4006)"
staticcheck is a static analysis tool for go.
It report several "this value of X is never used (SA4006)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
2020-05-02 23:08:01 +02:00
return fmt . Errorf ( "no tags options found for %s" , field . Name )
2016-09-23 16:19:07 +02:00
}
// the first one is the json tag
key := options [ 0 ]
if _ , okay := totalMap . Value ( key ) ; okay {
delete ( totalMap , key )
}
}
i = ( * IssueFields ) ( aux . Alias )
// all the tags found in the struct were removed. Whatever is left are unknowns to struct
i . Unknowns = totalMap
return nil
2015-09-03 12:25:21 +02:00
}
2020-05-14 17:18:31 +02:00
// IssueRenderedFields represents rendered fields of a Jira issue.
2018-06-03 23:42:41 +02:00
// Not all IssueFields are rendered.
type IssueRenderedFields struct {
// TODO Missing fields
// * "aggregatetimespent": null,
// * "workratio": -1,
// * "lastViewed": null,
// * "aggregatetimeoriginalestimate": null,
// * "aggregatetimeestimate": null,
// * "environment": null,
Resolutiondate string ` json:"resolutiondate,omitempty" structs:"resolutiondate,omitempty" `
Created string ` json:"created,omitempty" structs:"created,omitempty" `
Duedate string ` json:"duedate,omitempty" structs:"duedate,omitempty" `
Updated string ` json:"updated,omitempty" structs:"updated,omitempty" `
Comments * Comments ` json:"comment,omitempty" structs:"comment,omitempty" `
2019-04-22 13:43:06 +02:00
Description string ` json:"description,omitempty" structs:"description,omitempty" `
2018-06-03 23:42:41 +02:00
}
2020-05-14 17:18:31 +02:00
// IssueType represents a type of a Jira issue.
2015-09-03 12:25:21 +02:00
// Typical types are "Request", "Bug", "Story", ...
type IssueType struct {
2016-09-23 16:19:07 +02:00
Self string ` json:"self,omitempty" structs:"self,omitempty" `
2016-10-07 10:52:40 +02:00
ID string ` json:"id,omitempty" structs:"id,omitempty" `
Description string ` json:"description,omitempty" structs:"description,omitempty" `
IconURL string ` json:"iconUrl,omitempty" structs:"iconUrl,omitempty" `
Name string ` json:"name,omitempty" structs:"name,omitempty" `
Subtask bool ` json:"subtask,omitempty" structs:"subtask,omitempty" `
AvatarID int ` json:"avatarId,omitempty" structs:"avatarId,omitempty" `
2015-09-03 12:25:21 +02:00
}
2020-05-14 17:18:31 +02:00
// Watches represents a type of how many and which user are "observing" a Jira issue to track the status / updates.
2015-09-03 12:25:21 +02:00
type Watches struct {
2017-12-11 15:16:13 +02:00
Self string ` json:"self,omitempty" structs:"self,omitempty" `
WatchCount int ` json:"watchCount,omitempty" structs:"watchCount,omitempty" `
IsWatching bool ` json:"isWatching,omitempty" structs:"isWatching,omitempty" `
Watchers [ ] * Watcher ` json:"watchers,omitempty" structs:"watchers,omitempty" `
}
// Watcher represents a simplified user that "observes" the issue
type Watcher struct {
Self string ` json:"self,omitempty" structs:"self,omitempty" `
Name string ` json:"name,omitempty" structs:"name,omitempty" `
2020-03-10 18:03:59 +02:00
AccountID string ` json:"accountId,omitempty" structs:"accountId,omitempty" `
2017-12-11 15:16:13 +02:00
DisplayName string ` json:"displayName,omitempty" structs:"displayName,omitempty" `
Active bool ` json:"active,omitempty" structs:"active,omitempty" `
2016-06-03 23:14:27 +02:00
}
2016-06-03 23:25:18 +02:00
// AvatarUrls represents different dimensions of avatars / images
2016-06-03 23:14:27 +02:00
type AvatarUrls struct {
2016-09-23 16:19:07 +02:00
Four8X48 string ` json:"48x48,omitempty" structs:"48x48,omitempty" `
Two4X24 string ` json:"24x24,omitempty" structs:"24x24,omitempty" `
One6X16 string ` json:"16x16,omitempty" structs:"16x16,omitempty" `
Three2X32 string ` json:"32x32,omitempty" structs:"32x32,omitempty" `
2015-09-03 12:25:21 +02:00
}
2020-05-14 17:18:31 +02:00
// Component represents a "component" of a Jira issue.
// Components can be user defined in every Jira instance.
2015-09-03 12:25:21 +02:00
type Component struct {
2021-03-18 17:22:03 +02:00
Self string ` json:"self,omitempty" structs:"self,omitempty" `
ID string ` json:"id,omitempty" structs:"id,omitempty" `
Name string ` json:"name,omitempty" structs:"name,omitempty" `
Description string ` json:"description,omitempty" structs:"description,omitempty" `
2015-09-03 12:25:21 +02:00
}
2020-05-14 17:18:31 +02:00
// Progress represents the progress of a Jira issue.
2015-09-03 12:25:21 +02:00
type Progress struct {
2016-09-23 16:19:07 +02:00
Progress int ` json:"progress" structs:"progress" `
Total int ` json:"total" structs:"total" `
2018-07-20 14:26:26 +02:00
Percent int ` json:"percent" structs:"percent" `
2015-09-03 12:25:21 +02:00
}
2020-05-14 17:18:31 +02:00
// Parent represents the parent of a Jira issue, to be used with subtask issue types.
2016-11-01 02:31:51 +02:00
type Parent struct {
2016-11-04 21:44:23 +02:00
ID string ` json:"id,omitempty" structs:"id" `
Key string ` json:"key,omitempty" structs:"key" `
2015-09-03 12:25:21 +02:00
}
2020-05-14 17:18:31 +02:00
// Time represents the Time definition of Jira as a time.Time of go
2016-06-04 10:41:34 +02:00
type Time time . Time
2016-05-29 21:10:19 +02:00
2019-05-05 02:22:09 +02:00
func ( t Time ) Equal ( u Time ) bool {
return time . Time ( t ) . Equal ( time . Time ( u ) )
}
2020-05-14 17:18:31 +02:00
// Date represents the Date definition of Jira as a time.Time of go
2018-01-16 15:38:17 +02:00
type Date time . Time
2016-07-17 11:23:49 +02:00
// Wrapper struct for search result
type transitionResult struct {
2016-09-23 16:19:07 +02:00
Transitions [ ] Transition ` json:"transitions" structs:"transitions" `
2016-07-17 11:23:49 +02:00
}
2020-05-14 17:18:31 +02:00
// Transition represents an issue transition in Jira
2016-07-17 11:23:49 +02:00
type Transition struct {
2016-09-23 16:19:07 +02:00
ID string ` json:"id" structs:"id" `
Name string ` json:"name" structs:"name" `
2018-01-10 18:21:25 +02:00
To Status ` json:"to" structs:"status" `
2016-09-23 16:19:07 +02:00
Fields map [ string ] TransitionField ` json:"fields" structs:"fields" `
2016-07-17 11:23:49 +02:00
}
2017-05-01 15:03:03 +02:00
// TransitionField represents the value of one Transition
2016-07-17 11:23:49 +02:00
type TransitionField struct {
2016-09-23 16:19:07 +02:00
Required bool ` json:"required" structs:"required" `
2016-07-17 11:23:49 +02:00
}
2016-07-17 11:41:50 +02:00
// CreateTransitionPayload is used for creating new issue transitions
2016-07-17 11:23:49 +02:00
type CreateTransitionPayload struct {
2018-01-10 18:12:02 +02:00
Transition TransitionPayload ` json:"transition" structs:"transition" `
Fields TransitionPayloadFields ` json:"fields" structs:"fields" `
2016-07-17 11:23:49 +02:00
}
2017-05-01 15:03:03 +02:00
// TransitionPayload represents the request payload of Transition calls like DoTransition
2016-07-17 11:23:49 +02:00
type TransitionPayload struct {
2016-09-23 16:19:07 +02:00
ID string ` json:"id" structs:"id" `
2016-07-17 11:23:49 +02:00
}
2018-01-10 18:12:02 +02:00
// TransitionPayloadFields represents the fields that can be set when executing a transition
type TransitionPayloadFields struct {
Resolution * Resolution ` json:"resolution,omitempty" structs:"resolution,omitempty" `
}
2017-04-27 07:06:10 +02:00
// Option represents an option value in a SelectList or MultiSelect
// custom issue field
type Option struct {
Value string ` json:"value" structs:"value" `
2016-07-17 11:23:49 +02:00
}
2020-05-14 17:18:31 +02:00
// UnmarshalJSON will transform the Jira time into a time.Time
// during the transformation of the Jira JSON response
2016-06-04 10:41:34 +02:00
func ( t * Time ) UnmarshalJSON ( b [ ] byte ) error {
2018-01-10 15:51:30 +02:00
// Ignore null, like in the main JSON package.
if string ( b ) == "null" {
2018-01-16 15:38:17 +02:00
return nil
2018-01-10 15:51:30 +02:00
}
2016-05-29 17:30:45 +02:00
ti , err := time . Parse ( "\"2006-01-02T15:04:05.999-0700\"" , string ( b ) )
if err != nil {
return err
}
2016-06-04 10:41:34 +02:00
* t = Time ( ti )
2016-05-29 17:30:45 +02:00
return nil
2015-09-03 12:25:21 +02:00
}
2020-05-14 17:18:31 +02:00
// MarshalJSON will transform the time.Time into a Jira time
// during the creation of a Jira request
2018-06-27 13:32:48 +02:00
func ( t Time ) MarshalJSON ( ) ( [ ] byte , error ) {
2020-04-10 16:01:27 +02:00
return [ ] byte ( time . Time ( t ) . Format ( "\"2006-01-02T15:04:05.000-0700\"" ) ) , nil
2018-06-27 13:32:48 +02:00
}
2020-05-14 17:18:31 +02:00
// UnmarshalJSON will transform the Jira date into a time.Time
// during the transformation of the Jira JSON response
2018-01-16 15:38:17 +02:00
func ( t * Date ) UnmarshalJSON ( b [ ] byte ) error {
// Ignore null, like in the main JSON package.
if string ( b ) == "null" {
return nil
}
ti , err := time . Parse ( "\"2006-01-02\"" , string ( b ) )
if err != nil {
return err
}
* t = Date ( ti )
return nil
}
2018-01-19 18:57:08 +02:00
// MarshalJSON will transform the Date object into a short
2020-05-14 17:18:31 +02:00
// date string as Jira expects during the creation of a
// Jira request
2018-01-19 18:57:08 +02:00
func ( t Date ) MarshalJSON ( ) ( [ ] byte , error ) {
time := time . Time ( t )
return [ ] byte ( time . Format ( "\"2006-01-02\"" ) ) , nil
}
2020-05-14 17:18:31 +02:00
// Worklog represents the work log of a Jira issue.
2016-06-03 23:25:18 +02:00
// One Worklog contains zero or n WorklogRecords
2020-05-14 17:18:31 +02:00
// Jira Wiki: https://confluence.atlassian.com/jira/logging-work-on-an-issue-185729605.html
2015-09-03 12:25:21 +02:00
type Worklog struct {
2016-09-23 16:19:07 +02:00
StartAt int ` json:"startAt" structs:"startAt" `
MaxResults int ` json:"maxResults" structs:"maxResults" `
Total int ` json:"total" structs:"total" `
Worklogs [ ] WorklogRecord ` json:"worklogs" structs:"worklogs" `
2016-06-03 23:14:27 +02:00
}
2016-06-03 23:25:18 +02:00
// WorklogRecord represents one entry of a Worklog
2016-06-03 23:14:27 +02:00
type WorklogRecord struct {
2019-05-05 02:22:09 +02:00
Self string ` json:"self,omitempty" structs:"self,omitempty" `
Author * User ` json:"author,omitempty" structs:"author,omitempty" `
UpdateAuthor * User ` json:"updateAuthor,omitempty" structs:"updateAuthor,omitempty" `
Comment string ` json:"comment,omitempty" structs:"comment,omitempty" `
Created * Time ` json:"created,omitempty" structs:"created,omitempty" `
Updated * Time ` json:"updated,omitempty" structs:"updated,omitempty" `
Started * Time ` json:"started,omitempty" structs:"started,omitempty" `
TimeSpent string ` json:"timeSpent,omitempty" structs:"timeSpent,omitempty" `
TimeSpentSeconds int ` json:"timeSpentSeconds,omitempty" structs:"timeSpentSeconds,omitempty" `
ID string ` json:"id,omitempty" structs:"id,omitempty" `
IssueID string ` json:"issueId,omitempty" structs:"issueId,omitempty" `
Properties [ ] EntityProperty ` json:"properties,omitempty" `
}
type EntityProperty struct {
Key string ` json:"key" `
Value interface { } ` json:"value" `
2016-05-29 18:42:38 +02:00
}
2020-05-14 17:18:31 +02:00
// TimeTracking represents the timetracking fields of a Jira issue.
2016-10-24 11:06:37 +02:00
type TimeTracking struct {
2016-10-23 14:50:36 +02:00
OriginalEstimate string ` json:"originalEstimate,omitempty" structs:"originalEstimate,omitempty" `
RemainingEstimate string ` json:"remainingEstimate,omitempty" structs:"remainingEstimate,omitempty" `
TimeSpent string ` json:"timeSpent,omitempty" structs:"timeSpent,omitempty" `
OriginalEstimateSeconds int ` json:"originalEstimateSeconds,omitempty" structs:"originalEstimateSeconds,omitempty" `
RemainingEstimateSeconds int ` json:"remainingEstimateSeconds,omitempty" structs:"remainingEstimateSeconds,omitempty" `
TimeSpentSeconds int ` json:"timeSpentSeconds,omitempty" structs:"timeSpentSeconds,omitempty" `
2016-05-29 18:42:38 +02:00
}
2016-06-03 23:25:18 +02:00
// Subtasks represents all issues of a parent issue.
2016-05-29 18:42:38 +02:00
type Subtasks struct {
2016-09-23 16:19:07 +02:00
ID string ` json:"id" structs:"id" `
Key string ` json:"key" structs:"key" `
Self string ` json:"self" structs:"self" `
Fields IssueFields ` json:"fields" structs:"fields" `
2015-09-03 12:25:21 +02:00
}
2020-05-14 17:18:31 +02:00
// IssueLink represents a link between two issues in Jira.
2015-09-03 12:25:21 +02:00
type IssueLink struct {
2016-09-23 16:19:07 +02:00
ID string ` json:"id,omitempty" structs:"id,omitempty" `
Self string ` json:"self,omitempty" structs:"self,omitempty" `
Type IssueLinkType ` json:"type" structs:"type" `
OutwardIssue * Issue ` json:"outwardIssue" structs:"outwardIssue" `
InwardIssue * Issue ` json:"inwardIssue" structs:"inwardIssue" `
Comment * Comment ` json:"comment,omitempty" structs:"comment,omitempty" `
2015-09-03 12:25:21 +02:00
}
2020-05-14 17:18:31 +02:00
// IssueLinkType represents a type of a link between to issues in Jira.
2015-09-03 12:25:21 +02:00
// Typical issue link types are "Related to", "Duplicate", "Is blocked by", etc.
type IssueLinkType struct {
2016-09-23 16:19:07 +02:00
ID string ` json:"id,omitempty" structs:"id,omitempty" `
Self string ` json:"self,omitempty" structs:"self,omitempty" `
Name string ` json:"name" structs:"name" `
Inward string ` json:"inward" structs:"inward" `
Outward string ` json:"outward" structs:"outward" `
2015-09-03 12:25:21 +02:00
}
2016-07-08 01:21:14 +02:00
// Comments represents a list of Comment.
type Comments struct {
2016-09-23 16:19:07 +02:00
Comments [ ] * Comment ` json:"comments,omitempty" structs:"comments,omitempty" `
2016-07-08 01:21:14 +02:00
}
2020-05-14 17:18:31 +02:00
// Comment represents a comment by a person to an issue in Jira.
2015-09-03 12:25:21 +02:00
type Comment struct {
2016-09-23 16:19:07 +02:00
ID string ` json:"id,omitempty" structs:"id,omitempty" `
Self string ` json:"self,omitempty" structs:"self,omitempty" `
2016-10-23 14:50:36 +02:00
Name string ` json:"name,omitempty" structs:"name,omitempty" `
2016-09-23 16:19:07 +02:00
Author User ` json:"author,omitempty" structs:"author,omitempty" `
Body string ` json:"body,omitempty" structs:"body,omitempty" `
2021-10-25 12:04:28 +02:00
RenderedBody string ` json:"renderedBody,omitempty" structs:"body,omitempty" `
2016-09-23 16:19:07 +02:00
UpdateAuthor User ` json:"updateAuthor,omitempty" structs:"updateAuthor,omitempty" `
Updated string ` json:"updated,omitempty" structs:"updated,omitempty" `
Created string ` json:"created,omitempty" structs:"created,omitempty" `
Visibility CommentVisibility ` json:"visibility,omitempty" structs:"visibility,omitempty" `
2015-09-03 12:25:21 +02:00
}
2021-10-13 07:30:20 +02:00
// Updated struct for new v3 jira api's
type Commentv3 struct {
ID string ` json:"id,omitempty" structs:"id,omitempty" `
Self string ` json:"self,omitempty" structs:"self,omitempty" `
Name string ` json:"name,omitempty" structs:"name,omitempty" `
Author User ` json:"author,omitempty" structs:"author,omitempty" `
Body interface { } ` json:"body,omitempty" structs:"body,omitempty" `
UpdateAuthor User ` json:"updateAuthor,omitempty" structs:"updateAuthor,omitempty" `
Updated string ` json:"updated,omitempty" structs:"updated,omitempty" `
Created string ` json:"created,omitempty" structs:"created,omitempty" `
Visibility CommentVisibility ` json:"visibility,omitempty" structs:"visibility,omitempty" `
}
2016-02-14 05:15:32 +02:00
// FixVersion represents a software release in which an issue is fixed.
type FixVersion struct {
2019-04-28 19:10:41 +02:00
Self string ` json:"self,omitempty" structs:"self,omitempty" `
2016-09-23 16:19:07 +02:00
ID string ` json:"id,omitempty" structs:"id,omitempty" `
Name string ` json:"name,omitempty" structs:"name,omitempty" `
2019-07-05 16:31:53 +02:00
Description string ` json:"description,omitempty" structs:"description,omitempty" `
2019-04-28 19:10:41 +02:00
Archived * bool ` json:"archived,omitempty" structs:"archived,omitempty" `
2016-09-23 16:19:07 +02:00
Released * bool ` json:"released,omitempty" structs:"released,omitempty" `
2019-04-28 19:10:41 +02:00
ReleaseDate string ` json:"releaseDate,omitempty" structs:"releaseDate,omitempty" `
2016-09-23 16:19:07 +02:00
UserReleaseDate string ` json:"userReleaseDate,omitempty" structs:"userReleaseDate,omitempty" `
2019-04-28 19:10:41 +02:00
ProjectID int ` json:"projectId,omitempty" structs:"projectId,omitempty" ` // Unlike other IDs, this is returned as a number
StartDate string ` json:"startDate,omitempty" structs:"startDate,omitempty" `
2016-02-14 05:15:32 +02:00
}
2019-04-28 19:52:48 +02:00
// AffectsVersion represents a software release which is affected by an issue.
type AffectsVersion Version
2016-03-27 14:03:40 +02:00
// CommentVisibility represents he visibility of a comment.
// E.g. Type could be "role" and Value "Administrators"
type CommentVisibility struct {
2016-09-23 16:19:07 +02:00
Type string ` json:"type,omitempty" structs:"type,omitempty" `
Value string ` json:"value,omitempty" structs:"value,omitempty" `
2016-05-29 17:30:45 +02:00
}
2016-06-19 15:08:53 +02:00
// SearchOptions specifies the optional parameters to various List methods that
// support pagination.
2020-05-14 17:18:31 +02:00
// Pagination is used for the Jira REST APIs to conserve server resources and limit
2016-06-19 15:08:53 +02:00
// response size for resources that return potentially large collection of items.
// A request to a pages API will result in a values array wrapped in a JSON object with some paging metadata
// Default Pagination options
2016-06-16 11:10:30 +02:00
type SearchOptions struct {
2016-06-19 15:08:53 +02:00
// StartAt: The starting index of the returned projects. Base index: 0.
StartAt int ` url:"startAt,omitempty" `
// MaxResults: The maximum number of projects to return per page. Default: 50.
MaxResults int ` url:"maxResults,omitempty" `
2017-01-28 00:04:08 +02:00
// Expand: Expand specific sections in the returned issues
2017-02-24 02:48:06 +02:00
Expand string ` url:"expand,omitempty" `
2017-04-13 21:02:50 +02:00
Fields [ ] string
2018-03-15 15:52:16 +02:00
// ValidateQuery: The validateQuery param offers control over whether to validate and how strictly to treat the validation. Default: strict.
ValidateQuery string ` url:"validateQuery,omitempty" `
2016-06-16 11:10:30 +02:00
}
2017-01-29 18:28:04 +02:00
// searchResult is only a small wrapper around the Search (with JQL) method
2016-06-04 10:41:34 +02:00
// to be able to parse the results
type searchResult struct {
2021-08-27 10:23:42 +02:00
Issues [ ] Issue ` json:"issues" structs:"issues" `
StartAt int ` json:"startAt" structs:"startAt" `
MaxResults int ` json:"maxResults" structs:"maxResults" `
Total int ` json:"total" structs:"total" `
Names map [ string ] string ` json:"names,omitempty" structs:"names,omitempty" `
2016-06-04 10:41:34 +02:00
}
2017-01-28 00:04:08 +02:00
// GetQueryOptions specifies the optional parameters for the Get Issue methods
type GetQueryOptions struct {
// Fields is the list of fields to return for the issue. By default, all fields are returned.
Fields string ` url:"fields,omitempty" `
Expand string ` url:"expand,omitempty" `
// Properties is the list of properties to return for the issue. By default no properties are returned.
Properties string ` url:"properties,omitempty" `
// FieldsByKeys if true then fields in issues will be referenced by keys instead of ids
2018-03-05 15:56:57 +02:00
FieldsByKeys bool ` url:"fieldsByKeys,omitempty" `
UpdateHistory bool ` url:"updateHistory,omitempty" `
ProjectKeys string ` url:"projectKeys,omitempty" `
2016-06-04 10:41:34 +02:00
}
2019-05-05 02:22:09 +02:00
// GetWorklogsQueryOptions specifies the optional parameters for the Get Worklogs method
type GetWorklogsQueryOptions struct {
2021-02-09 11:17:23 +02:00
StartAt int64 ` url:"startAt,omitempty" `
MaxResults int32 ` url:"maxResults,omitempty" `
StartedAfter int64 ` url:"startedAfter,omitempty" `
Expand string ` url:"expand,omitempty" `
2019-05-05 02:22:09 +02:00
}
type AddWorklogQueryOptions struct {
NotifyUsers bool ` url:"notifyUsers,omitempty" `
AdjustEstimate string ` url:"adjustEstimate,omitempty" `
NewEstimate string ` url:"newEstimate,omitempty" `
ReduceBy string ` url:"reduceBy,omitempty" `
Expand string ` url:"expand,omitempty" `
OverrideEditableFlag bool ` url:"overrideEditableFlag,omitempty" `
}
2020-05-14 17:18:31 +02:00
// CustomFields represents custom fields of Jira
// This can heavily differ between Jira instances
2016-06-04 10:41:34 +02:00
type CustomFields map [ string ] string
2019-12-11 15:50:22 +02:00
// RemoteLink represents remote links which linked to issues
type RemoteLink struct {
ID int ` json:"id,omitempty" structs:"id,omitempty" `
Self string ` json:"self,omitempty" structs:"self,omitempty" `
GlobalID string ` json:"globalId,omitempty" structs:"globalId,omitempty" `
Application * RemoteLinkApplication ` json:"application,omitempty" structs:"application,omitempty" `
Relationship string ` json:"relationship,omitempty" structs:"relationship,omitempty" `
Object * RemoteLinkObject ` json:"object,omitempty" structs:"object,omitempty" `
}
// RemoteLinkApplication represents remote links application
type RemoteLinkApplication struct {
Type string ` json:"type,omitempty" structs:"type,omitempty" `
Name string ` json:"name,omitempty" structs:"name,omitempty" `
}
// RemoteLinkObject represents remote link object itself
type RemoteLinkObject struct {
URL string ` json:"url,omitempty" structs:"url,omitempty" `
Title string ` json:"title,omitempty" structs:"title,omitempty" `
Summary string ` json:"summary,omitempty" structs:"summary,omitempty" `
Icon * RemoteLinkIcon ` json:"icon,omitempty" structs:"icon,omitempty" `
Status * RemoteLinkStatus ` json:"status,omitempty" structs:"status,omitempty" `
}
// RemoteLinkIcon represents icon displayed next to link
type RemoteLinkIcon struct {
Url16x16 string ` json:"url16x16,omitempty" structs:"url16x16,omitempty" `
Title string ` json:"title,omitempty" structs:"title,omitempty" `
Link string ` json:"link,omitempty" structs:"link,omitempty" `
}
// RemoteLinkStatus if the link is a resolvable object (issue, epic) - the structure represent its status
type RemoteLinkStatus struct {
2020-06-18 22:13:01 +02:00
Resolved bool ` json:"resolved,omitempty" structs:"resolved,omitempty" `
Icon * RemoteLinkIcon ` json:"icon,omitempty" structs:"icon,omitempty" `
2019-12-11 15:50:22 +02:00
}
2021-11-22 14:54:06 +02:00
// type for rank api
type RankBeforeReqBody struct {
Issues [ ] string ` json:"issues" `
RankBeforeIssue string ` json:"rankBeforeIssue" `
RankCustomFieldId int64 ` json:"rankCustomFieldId" `
}
type RankAfterReqBody struct {
Issues [ ] string ` json:"issues" `
RankAfterIssue string ` json:"rankAfterIssue" `
RankCustomFieldId int64 ` json:"rankCustomFieldId" `
}
2020-05-03 15:38:32 +02:00
// GetWithContext returns a full representation of the issue for the given issue key.
2020-05-14 17:18:31 +02:00
// Jira will attempt to identify the issue by the issueIdOrKey path parameter.
2015-09-03 12:25:21 +02:00
// This can be an issue id, or an issue key.
2020-05-14 17:18:31 +02:00
// 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.
2015-09-03 12:25:21 +02:00
//
2017-01-28 00:04:08 +02:00
// The given options will be appended to the query string
//
2020-05-14 17:18:31 +02:00
// Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue-getIssue
2020-05-03 15:38:32 +02:00
func ( s * IssueService ) GetWithContext ( ctx context . Context , issueID string , options * GetQueryOptions ) ( * Issue , * Response , error ) {
2018-03-13 11:49:27 +02:00
apiEndpoint := fmt . Sprintf ( "rest/api/2/issue/%s" , issueID )
2020-05-03 15:38:32 +02:00
req , err := s . client . NewRequestWithContext ( ctx , "GET" , apiEndpoint , nil )
2018-03-13 11:49:27 +02:00
if err != nil {
return nil , nil , err
}
if options != nil {
q , err := query . Values ( options )
if err != nil {
return nil , nil , err
}
req . URL . RawQuery = q . Encode ( )
}
issue := new ( Issue )
resp , err := s . client . Do ( req , issue )
if err != nil {
jerr := NewJiraError ( resp , err )
return nil , resp , jerr
}
return issue , resp , nil
}
2020-05-03 15:38:32 +02:00
// 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.
2016-06-15 17:09:13 +02:00
// The attachment is in the Response.Body of the response.
2016-05-27 14:14:09 +02:00
// This is an io.ReadCloser.
// The caller should close the resp.Body.
2020-05-03 15:38:32 +02:00
func ( s * IssueService ) DownloadAttachmentWithContext ( ctx context . Context , attachmentID string ) ( * Response , error ) {
2016-05-19 23:11:21 +02:00
apiEndpoint := fmt . Sprintf ( "secure/attachment/%s/" , attachmentID )
2020-05-03 15:38:32 +02:00
req , err := s . client . NewRequestWithContext ( ctx , "GET" , apiEndpoint , nil )
2016-05-29 17:30:45 +02:00
if err != nil {
2016-05-19 23:11:21 +02:00
return nil , err
2016-05-29 17:30:45 +02:00
}
2016-05-27 14:14:09 +02:00
resp , err := s . client . Do ( req , nil )
2016-05-29 17:30:45 +02:00
if err != nil {
2017-11-04 00:22:32 +02:00
jerr := NewJiraError ( resp , err )
return resp , jerr
2016-05-29 17:30:45 +02:00
}
2016-05-19 23:11:21 +02:00
return resp , nil
}
2020-05-03 15:38:32 +02:00
// 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 ) {
2017-05-06 14:09:22 +02:00
apiEndpoint := fmt . Sprintf ( "rest/api/2/issue/%s/attachments" , issueID )
2016-05-19 23:11:21 +02:00
b := new ( bytes . Buffer )
writer := multipart . NewWriter ( b )
fw , err := writer . CreateFormFile ( "file" , attachmentName )
if err != nil {
return nil , nil , err
2016-05-29 17:30:45 +02:00
}
2016-05-25 09:47:10 +02:00
if r != nil {
// Copy the file
if _ , err = io . Copy ( fw , r ) ; err != nil {
return nil , nil , err
2016-05-29 17:30:45 +02:00
}
}
2016-05-19 23:11:21 +02:00
writer . Close ( )
2020-05-03 15:38:32 +02:00
req , err := s . client . NewMultiPartRequestWithContext ( ctx , "POST" , apiEndpoint , b )
2016-05-19 23:11:21 +02:00
if err != nil {
return nil , nil , err
}
req . Header . Set ( "Content-Type" , writer . FormDataContentType ( ) )
2016-05-25 09:04:23 +02:00
// PostAttachment response returns a JSON array (as multiple attachments can be posted)
attachment := new ( [ ] Attachment )
resp , err := s . client . Do ( req , attachment )
2016-05-19 23:11:21 +02:00
if err != nil {
2017-11-04 00:22:32 +02:00
jerr := NewJiraError ( resp , err )
return nil , resp , jerr
2016-05-19 23:11:21 +02:00
}
2016-05-25 09:04:23 +02:00
return attachment , resp , nil
2016-05-29 17:30:45 +02:00
}
2020-05-03 15:38:32 +02:00
// 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 ) {
2019-04-17 05:27:33 +02:00
apiEndpoint := fmt . Sprintf ( "rest/api/2/attachment/%s" , attachmentID )
2020-05-03 15:38:32 +02:00
req , err := s . client . NewRequestWithContext ( ctx , "DELETE" , apiEndpoint , nil )
2019-04-17 05:27:33 +02:00
if err != nil {
return nil , err
}
resp , err := s . client . Do ( req , nil )
if err != nil {
jerr := NewJiraError ( resp , err )
return resp , jerr
}
return resp , nil
}
2020-05-03 15:38:32 +02:00
// DeleteAttachment wraps DeleteAttachmentWithContext using the background context.
func ( s * IssueService ) DeleteAttachment ( attachmentID string ) ( * Response , error ) {
return s . DeleteAttachmentWithContext ( context . Background ( ) , attachmentID )
}
2021-02-06 17:09:14 +02:00
// DeleteLinkWithContext deletes a link of a given linkID
func ( s * IssueService ) DeleteLinkWithContext ( ctx context . Context , linkID string ) ( * Response , error ) {
apiEndpoint := fmt . Sprintf ( "rest/api/2/issueLink/%s" , linkID )
req , err := s . client . NewRequestWithContext ( ctx , "DELETE" , apiEndpoint , nil )
if err != nil {
return nil , err
}
resp , err := s . client . Do ( req , nil )
if err != nil {
jerr := NewJiraError ( resp , err )
return resp , jerr
}
return resp , nil
}
// DeleteLink wraps DeleteLinkWithContext using the background context.
func ( s * IssueService ) DeleteLink ( linkID string ) ( * Response , error ) {
return s . DeleteLinkWithContext ( context . Background ( ) , linkID )
}
2020-05-03 15:38:32 +02:00
// GetWorklogsWithContext gets all the worklogs for an issue.
2017-10-27 23:21:47 +02:00
// 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
2020-05-03 15:38:32 +02:00
func ( s * IssueService ) GetWorklogsWithContext ( ctx context . Context , issueID string , options ... func ( * http . Request ) error ) ( * Worklog , * Response , error ) {
2017-10-27 23:21:47 +02:00
apiEndpoint := fmt . Sprintf ( "rest/api/2/issue/%s/worklog" , issueID )
2020-05-03 15:38:32 +02:00
req , err := s . client . NewRequestWithContext ( ctx , "GET" , apiEndpoint , nil )
2017-10-27 23:21:47 +02:00
if err != nil {
return nil , nil , err
}
2019-05-05 02:22:09 +02:00
for _ , option := range options {
err = option ( req )
if err != nil {
return nil , nil , err
}
}
2017-10-27 23:21:47 +02:00
v := new ( Worklog )
resp , err := s . client . Do ( req , v )
return v , resp , err
}
2021-11-22 14:54:06 +02:00
// Rank rank's given issue
func ( s * IssueService ) Rank ( issueIDs [ ] string , beforeIssue , afterIssue string , customFieldId int64 ) error {
apiEndpoint := "/rest/agile/1.0/issue/rank"
var reqBody interface { }
if len ( beforeIssue ) > 0 {
reqBody = & RankBeforeReqBody {
Issues : issueIDs ,
RankBeforeIssue : beforeIssue ,
RankCustomFieldId : customFieldId ,
}
} else {
reqBody = & RankAfterReqBody {
Issues : issueIDs ,
2021-12-08 15:30:13 +02:00
RankAfterIssue : afterIssue ,
2021-11-22 14:54:06 +02:00
RankCustomFieldId : customFieldId ,
}
}
req , err := s . client . NewRequestWithContext ( context . Background ( ) , "PUT" , apiEndpoint , reqBody )
if err != nil {
return err
}
_ , err = s . client . Do ( req , nil )
if err != nil {
return err
}
return nil
}
2020-05-03 15:38:32 +02:00
// 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 ... )
}
2019-05-05 02:22:09 +02:00
// Applies query options to http request.
// This helper is meant to be used with all "QueryOptions" structs.
func WithQueryOptions ( options interface { } ) func ( * http . Request ) error {
q , err := query . Values ( options )
if err != nil {
return func ( * http . Request ) error {
return err
}
}
return func ( r * http . Request ) error {
r . URL . RawQuery = q . Encode ( )
return nil
}
}
2020-05-03 15:38:32 +02:00
// CreateWithContext creates an issue or a sub-task from a JSON representation.
2015-09-03 12:25:21 +02:00
// 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.
//
2020-05-14 17:18:31 +02:00
// Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue-createIssues
2020-05-03 15:38:32 +02:00
func ( s * IssueService ) CreateWithContext ( ctx context . Context , issue * Issue ) ( * Issue , * Response , error ) {
2018-08-10 17:11:53 +02:00
apiEndpoint := "rest/api/2/issue"
2020-05-03 15:38:32 +02:00
req , err := s . client . NewRequestWithContext ( ctx , "POST" , apiEndpoint , issue )
2015-09-03 12:25:21 +02:00
if err != nil {
return nil , nil , err
}
2016-10-05 14:53:30 +02:00
resp , err := s . client . Do ( req , nil )
2015-09-03 12:25:21 +02:00
if err != nil {
2016-10-05 14:53:30 +02:00
// incase of error return the resp for further inspection
2015-09-03 12:25:21 +02:00
return nil , resp , err
}
responseIssue := new ( Issue )
2016-10-05 14:53:30 +02:00
defer resp . Body . Close ( )
data , err := ioutil . ReadAll ( resp . Body )
if err != nil {
style: Fix staticcheck (static analysis) errors for this library (#283)
* style: Fix staticcheck errors for "error strings should not be capitalized (ST1005)"
staticcheck is a static analysis tool for go.
It reports several "error strings should not be capitalized (ST1005)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "printf-style function with dynamic format ... (SA1006)"
staticcheck is a static analysis tool for go.
It reports several "printf-style function with dynamic format string and no further arguments should use print-style function instead (SA1006)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "type X is unused (U1000)"
staticcheck is a static analysis tool for go.
It reports several "type X is unused (U1000)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "should use X instead (S1003 & SA6005)"
staticcheck is a static analysis tool for go.
It reports several
- should use !bytes.Contains(b, []byte(`"password":"bar"`)) instead (S1003)
- should use strings.EqualFold instead (SA6005)
messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "unnecessary use of fmt.Sprintf (S1039)"
staticcheck is a static analysis tool for go.
It report several "unnecessary use of fmt.Sprintf (S1039)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "this value of X is never used (SA4006)"
staticcheck is a static analysis tool for go.
It report several "this value of X is never used (SA4006)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "redundant return statement (S1023)"
staticcheck is a static analysis tool for go.
It report several "redundant return statement (S1023)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "possible nil pointer dereference (SA5011)"
staticcheck is a static analysis tool for go.
It report several
file.go:Line:character: possible nil pointer dereference (SA5011)
file.go:Line:character: this check suggests that the pointer can be nil
messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "this value of X is never used (SA4006)"
staticcheck is a static analysis tool for go.
It report several "this value of X is never used (SA4006)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
2020-05-02 23:08:01 +02:00
return nil , resp , fmt . Errorf ( "could not read the returned data" )
2016-10-05 14:53:30 +02:00
}
err = json . Unmarshal ( data , responseIssue )
if err != nil {
style: Fix staticcheck (static analysis) errors for this library (#283)
* style: Fix staticcheck errors for "error strings should not be capitalized (ST1005)"
staticcheck is a static analysis tool for go.
It reports several "error strings should not be capitalized (ST1005)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "printf-style function with dynamic format ... (SA1006)"
staticcheck is a static analysis tool for go.
It reports several "printf-style function with dynamic format string and no further arguments should use print-style function instead (SA1006)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "type X is unused (U1000)"
staticcheck is a static analysis tool for go.
It reports several "type X is unused (U1000)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "should use X instead (S1003 & SA6005)"
staticcheck is a static analysis tool for go.
It reports several
- should use !bytes.Contains(b, []byte(`"password":"bar"`)) instead (S1003)
- should use strings.EqualFold instead (SA6005)
messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "unnecessary use of fmt.Sprintf (S1039)"
staticcheck is a static analysis tool for go.
It report several "unnecessary use of fmt.Sprintf (S1039)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "this value of X is never used (SA4006)"
staticcheck is a static analysis tool for go.
It report several "this value of X is never used (SA4006)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "redundant return statement (S1023)"
staticcheck is a static analysis tool for go.
It report several "redundant return statement (S1023)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "possible nil pointer dereference (SA5011)"
staticcheck is a static analysis tool for go.
It report several
file.go:Line:character: possible nil pointer dereference (SA5011)
file.go:Line:character: this check suggests that the pointer can be nil
messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "this value of X is never used (SA4006)"
staticcheck is a static analysis tool for go.
It report several "this value of X is never used (SA4006)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
2020-05-02 23:08:01 +02:00
return nil , resp , fmt . Errorf ( "could not unmarshall the data into struct" )
2016-10-05 14:53:30 +02:00
}
2015-09-03 12:25:21 +02:00
return responseIssue , resp , nil
}
2016-01-05 16:22:21 +02:00
2020-05-03 15:38:32 +02:00
// 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,
2020-05-03 17:46:02 +02:00
// while also specifying query params. The issue is found by key.
2017-06-09 21:46:55 +02:00
//
2020-05-14 17:18:31 +02:00
// Jira API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/issue-editIssue
2020-05-03 15:38:32 +02:00
func ( s * IssueService ) UpdateWithOptionsWithContext ( ctx context . Context , issue * Issue , opts * UpdateQueryOptions ) ( * Issue , * Response , error ) {
2017-06-26 16:10:19 +02:00
apiEndpoint := fmt . Sprintf ( "rest/api/2/issue/%v" , issue . Key )
2018-10-17 21:17:08 +02:00
url , err := addOptions ( apiEndpoint , opts )
if err != nil {
return nil , nil , err
}
2020-05-03 15:38:32 +02:00
req , err := s . client . NewRequestWithContext ( ctx , "PUT" , url , issue )
2017-06-09 21:46:55 +02:00
if err != nil {
return nil , nil , err
}
resp , err := s . client . Do ( req , nil )
2015-09-03 12:25:21 +02:00
if err != nil {
2017-11-04 00:22:32 +02:00
jerr := NewJiraError ( resp , err )
return nil , resp , jerr
2015-09-03 12:25:21 +02:00
}
2017-06-09 21:46:55 +02:00
// This is just to follow the rest of the API's convention of returning an issue.
// Returning the same pointer here is pointless, so we return a copy instead.
ret := * issue
return & ret , resp , nil
}
2020-05-03 15:38:32 +02:00
// 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.
2017-06-09 21:46:55 +02:00
//
2020-05-14 17:18:31 +02:00
// Jira API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/issue-editIssue
2020-05-03 15:38:32 +02:00
func ( s * IssueService ) UpdateWithContext ( ctx context . Context , issue * Issue ) ( * Issue , * Response , error ) {
2021-03-16 19:04:03 +02:00
return s . UpdateWithOptionsWithContext ( ctx , issue , nil )
2017-06-09 21:46:55 +02:00
}
2020-05-03 15:38:32 +02:00
// 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.
2017-08-03 11:57:24 +02:00
//
// https://docs.atlassian.com/jira/REST/7.4.0/#api/2/issue-editIssue
2020-05-03 15:38:32 +02:00
func ( s * IssueService ) UpdateIssueWithContext ( ctx context . Context , jiraID string , data map [ string ] interface { } ) ( * Response , error ) {
2018-03-01 08:46:54 +02:00
apiEndpoint := fmt . Sprintf ( "rest/api/2/issue/%v" , jiraID )
2020-05-03 15:38:32 +02:00
req , err := s . client . NewRequestWithContext ( ctx , "PUT" , apiEndpoint , data )
2017-08-03 11:57:24 +02:00
if err != nil {
return nil , err
}
resp , err := s . client . Do ( req , nil )
if err != nil {
return resp , err
}
// This is just to follow the rest of the API's convention of returning an issue.
// Returning the same pointer here is pointless, so we return a copy instead.
return resp , nil
2015-09-03 12:25:21 +02:00
}
2016-01-05 16:22:21 +02:00
2020-05-03 15:38:32 +02:00
// 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 )
}
2021-10-20 08:51:50 +02:00
func ( s * IssueService ) GetComment ( ctx context . Context , issueID string , commentID string , options * GetQueryOptions ) ( * Comment , * Response , error ) {
apiEndpoint := fmt . Sprintf ( "rest/api/2/issue/%s/comment/%s" , issueID , commentID )
req , err := s . client . NewRequestWithContext ( ctx , "GET" , apiEndpoint , nil )
if err != nil {
return nil , nil , err
}
if options != nil {
q , err := query . Values ( options )
if err != nil {
2021-10-20 11:49:54 +02:00
return nil , nil , nil
2021-10-20 08:51:50 +02:00
}
req . URL . RawQuery = q . Encode ( )
}
comment := new ( Comment )
resp , err := s . client . Do ( req , comment )
if err != nil {
jerr := NewJiraError ( resp , err )
return nil , resp , jerr
}
2021-10-20 12:04:48 +02:00
return comment , resp , nil
2021-10-20 08:51:50 +02:00
}
2021-10-13 07:30:20 +02:00
// AddCommentWithContext adds a new comment to issueID
//
// https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-comments/#api-rest-api-3-comment-list-post
//
// Version 3
func ( s * IssueService ) AddCommentWithContextv3 ( ctx context . Context , issueID string , comment * Commentv3 ) ( * Commentv3 , * Response , error ) {
apiEndpoint := fmt . Sprintf ( "rest/api/3/issue/%s/comment" , issueID )
req , err := s . client . NewRequestWithContext ( ctx , "POST" , apiEndpoint , comment )
if err != nil {
return nil , nil , err
}
responseComment := new ( Commentv3 )
resp , err := s . client . Do ( req , responseComment )
if err != nil {
jerr := NewJiraError ( resp , err )
return nil , resp , jerr
}
return responseComment , resp , nil
}
// AddComment wraps AddCommentWithContext using the background context.
func ( s * IssueService ) AddCommentv3 ( issueID string , comment * Commentv3 ) ( * Commentv3 , * Response , error ) {
return s . AddCommentWithContextv3 ( context . Background ( ) , issueID , comment )
}
2020-05-03 15:38:32 +02:00
// AddCommentWithContext adds a new comment to issueID.
2016-03-27 14:03:40 +02:00
//
2020-05-14 17:18:31 +02:00
// Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue-addComment
2020-05-03 15:38:32 +02:00
func ( s * IssueService ) AddCommentWithContext ( ctx context . Context , issueID string , comment * Comment ) ( * Comment , * Response , error ) {
2016-01-05 16:22:21 +02:00
apiEndpoint := fmt . Sprintf ( "rest/api/2/issue/%s/comment" , issueID )
2020-05-03 15:38:32 +02:00
req , err := s . client . NewRequestWithContext ( ctx , "POST" , apiEndpoint , comment )
2016-01-05 16:22:21 +02:00
if err != nil {
return nil , nil , err
}
2016-03-27 14:03:40 +02:00
responseComment := new ( Comment )
2016-02-29 09:29:44 +02:00
resp , err := s . client . Do ( req , responseComment )
if err != nil {
2017-11-04 00:22:32 +02:00
jerr := NewJiraError ( resp , err )
return nil , resp , jerr
2016-02-29 09:29:44 +02:00
}
2016-01-05 16:22:21 +02:00
2016-03-27 14:03:40 +02:00
return responseComment , resp , nil
}
2016-02-29 09:29:44 +02:00
2020-05-03 15:38:32 +02:00
// 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 )
}
2021-10-13 12:09:08 +02:00
func ( s * IssueService ) UpdateCommentWithContextv3 ( ctx context . Context , issueID string , comment * Commentv3 ) ( * Commentv3 , * Response , error ) {
reqBody := struct {
2021-10-13 12:10:03 +02:00
Body interface { } ` json:"body" `
2021-10-13 12:09:08 +02:00
} {
2021-10-13 12:10:03 +02:00
Body : comment . Body ,
2021-10-13 12:09:08 +02:00
}
apiEndpoint := fmt . Sprintf ( "rest/api/3/issue/%s/comment/%s" , issueID , comment . ID )
req , err := s . client . NewRequestWithContext ( ctx , "PUT" , apiEndpoint , reqBody )
if err != nil {
return nil , nil , err
}
responseComment := new ( Commentv3 )
resp , err := s . client . Do ( req , responseComment )
if err != nil {
return nil , resp , err
}
return responseComment , resp , nil
}
// UpdateComment wraps UpdateCommentWithContext using the background context.
func ( s * IssueService ) UpdateCommentv3 ( issueID string , comment * Commentv3 ) ( * Commentv3 , * Response , error ) {
return s . UpdateCommentWithContextv3 ( context . Background ( ) , issueID , comment )
}
2020-05-03 15:38:32 +02:00
// UpdateCommentWithContext updates the body of a comment, identified by comment.ID, on the issueID.
2017-08-31 23:46:04 +02:00
//
2020-05-14 17:18:31 +02:00
// Jira API docs: https://docs.atlassian.com/jira/REST/cloud/#api/2/issue/{issueIdOrKey}/comment-updateComment
2020-05-03 15:38:32 +02:00
func ( s * IssueService ) UpdateCommentWithContext ( ctx context . Context , issueID string , comment * Comment ) ( * Comment , * Response , error ) {
2017-08-31 23:46:04 +02:00
reqBody := struct {
Body string ` json:"body" `
} {
Body : comment . Body ,
}
apiEndpoint := fmt . Sprintf ( "rest/api/2/issue/%s/comment/%s" , issueID , comment . ID )
2020-05-03 15:38:32 +02:00
req , err := s . client . NewRequestWithContext ( ctx , "PUT" , apiEndpoint , reqBody )
2017-08-31 23:46:04 +02:00
if err != nil {
return nil , nil , err
}
responseComment := new ( Comment )
resp , err := s . client . Do ( req , responseComment )
2016-02-29 09:29:44 +02:00
if err != nil {
return nil , resp , err
}
2016-01-05 16:22:21 +02:00
2016-03-27 14:03:40 +02:00
return responseComment , resp , nil
}
2016-02-29 09:29:44 +02:00
2020-05-03 15:38:32 +02:00
// 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.
2018-09-14 21:11:08 +02:00
//
2020-05-14 17:18:31 +02:00
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-api-3-issue-issueIdOrKey-comment-id-delete
2020-05-03 15:38:32 +02:00
func ( s * IssueService ) DeleteCommentWithContext ( ctx context . Context , issueID , commentID string ) error {
2018-09-14 21:11:08 +02:00
apiEndpoint := fmt . Sprintf ( "rest/api/2/issue/%s/comment/%s" , issueID , commentID )
2020-05-03 15:38:32 +02:00
req , err := s . client . NewRequestWithContext ( ctx , "DELETE" , apiEndpoint , nil )
2018-09-14 21:11:08 +02:00
if err != nil {
return err
}
2018-09-17 17:44:35 +02:00
resp , err := s . client . Do ( req , nil )
2018-09-14 21:11:08 +02:00
if err != nil {
2018-09-17 17:44:35 +02:00
jerr := NewJiraError ( resp , err )
return jerr
2018-09-14 21:11:08 +02:00
}
return nil
}
2020-05-03 15:38:32 +02:00
// 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.
2018-02-17 14:09:23 +02:00
//
// https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-issue-issueIdOrKey-worklog-post
2020-05-03 15:38:32 +02:00
func ( s * IssueService ) AddWorklogRecordWithContext ( ctx context . Context , issueID string , record * WorklogRecord , options ... func ( * http . Request ) error ) ( * WorklogRecord , * Response , error ) {
2018-03-01 08:46:54 +02:00
apiEndpoint := fmt . Sprintf ( "rest/api/2/issue/%s/worklog" , issueID )
2020-05-03 15:38:32 +02:00
req , err := s . client . NewRequestWithContext ( ctx , "POST" , apiEndpoint , record )
2018-03-01 08:46:54 +02:00
if err != nil {
return nil , nil , err
}
2018-02-17 14:09:23 +02:00
2019-05-05 02:22:09 +02:00
for _ , option := range options {
err = option ( req )
if err != nil {
return nil , nil , err
}
}
2019-11-13 23:17:21 +02:00
responseRecord := new ( WorklogRecord )
resp , err := s . client . Do ( req , responseRecord )
if err != nil {
jerr := NewJiraError ( resp , err )
return nil , resp , jerr
}
return responseRecord , resp , nil
}
2020-05-03 15:38:32 +02:00
// 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.
2019-11-13 23:17:21 +02:00
//
// https://docs.atlassian.com/software/jira/docs/api/REST/7.1.2/#api/2/issue-updateWorklog
2020-05-03 15:38:32 +02:00
func ( s * IssueService ) UpdateWorklogRecordWithContext ( ctx context . Context , issueID , worklogID string , record * WorklogRecord , options ... func ( * http . Request ) error ) ( * WorklogRecord , * Response , error ) {
2019-11-13 23:17:21 +02:00
apiEndpoint := fmt . Sprintf ( "rest/api/2/issue/%s/worklog/%s" , issueID , worklogID )
2020-05-03 15:38:32 +02:00
req , err := s . client . NewRequestWithContext ( ctx , "PUT" , apiEndpoint , record )
2019-11-13 23:17:21 +02:00
if err != nil {
return nil , nil , err
}
for _ , option := range options {
err = option ( req )
if err != nil {
return nil , nil , err
}
}
2018-03-01 08:46:54 +02:00
responseRecord := new ( WorklogRecord )
resp , err := s . client . Do ( req , responseRecord )
if err != nil {
jerr := NewJiraError ( resp , err )
return nil , resp , jerr
}
2018-02-17 14:09:23 +02:00
2018-03-01 08:46:54 +02:00
return responseRecord , resp , nil
2018-02-17 14:09:23 +02:00
}
2020-05-03 15:38:32 +02:00
// 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.
2016-02-29 09:29:44 +02:00
//
2020-05-14 17:18:31 +02:00
// Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issueLink
2020-05-03 15:38:32 +02:00
func ( s * IssueService ) AddLinkWithContext ( ctx context . Context , issueLink * IssueLink ) ( * Response , error ) {
style: Fix staticcheck (static analysis) errors for this library (#283)
* style: Fix staticcheck errors for "error strings should not be capitalized (ST1005)"
staticcheck is a static analysis tool for go.
It reports several "error strings should not be capitalized (ST1005)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "printf-style function with dynamic format ... (SA1006)"
staticcheck is a static analysis tool for go.
It reports several "printf-style function with dynamic format string and no further arguments should use print-style function instead (SA1006)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "type X is unused (U1000)"
staticcheck is a static analysis tool for go.
It reports several "type X is unused (U1000)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "should use X instead (S1003 & SA6005)"
staticcheck is a static analysis tool for go.
It reports several
- should use !bytes.Contains(b, []byte(`"password":"bar"`)) instead (S1003)
- should use strings.EqualFold instead (SA6005)
messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "unnecessary use of fmt.Sprintf (S1039)"
staticcheck is a static analysis tool for go.
It report several "unnecessary use of fmt.Sprintf (S1039)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "this value of X is never used (SA4006)"
staticcheck is a static analysis tool for go.
It report several "this value of X is never used (SA4006)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "redundant return statement (S1023)"
staticcheck is a static analysis tool for go.
It report several "redundant return statement (S1023)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "possible nil pointer dereference (SA5011)"
staticcheck is a static analysis tool for go.
It report several
file.go:Line:character: possible nil pointer dereference (SA5011)
file.go:Line:character: this check suggests that the pointer can be nil
messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "this value of X is never used (SA4006)"
staticcheck is a static analysis tool for go.
It report several "this value of X is never used (SA4006)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
2020-05-02 23:08:01 +02:00
apiEndpoint := "rest/api/2/issueLink"
2020-05-03 15:38:32 +02:00
req , err := s . client . NewRequestWithContext ( ctx , "POST" , apiEndpoint , issueLink )
2016-02-29 09:29:44 +02:00
if err != nil {
return nil , err
}
resp , err := s . client . Do ( req , nil )
2017-11-04 00:22:32 +02:00
if err != nil {
err = NewJiraError ( resp , err )
}
2016-02-29 09:29:44 +02:00
return resp , err
}
2016-05-29 17:30:45 +02:00
2020-05-03 15:38:32 +02:00
// 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
2016-06-04 10:41:34 +02:00
//
2020-05-14 17:18:31 +02:00
// Jira API docs: https://developer.atlassian.com/jiradev/jira-apis/jira-rest-apis/jira-rest-api-tutorials/jira-rest-api-example-query-issues
2021-08-27 20:49:33 +02:00
func ( s * IssueService ) SearchWithContext ( ctx context . Context , jql string , options * SearchOptions ) ( [ ] Issue , map [ string ] string , * Response , error ) {
2020-05-02 23:14:19 +02:00
u := url . URL {
Path : "rest/api/2/search" ,
}
uv := url . Values { }
if jql != "" {
2020-05-03 10:05:39 +02:00
uv . Add ( "jql" , jql )
2020-05-02 23:14:19 +02:00
}
if options != nil {
2019-05-21 05:46:20 +02:00
if options . StartAt != 0 {
2020-05-02 23:14:19 +02:00
uv . Add ( "startAt" , strconv . Itoa ( options . StartAt ) )
2019-05-21 05:46:20 +02:00
}
if options . MaxResults != 0 {
2020-05-02 23:14:19 +02:00
uv . Add ( "maxResults" , strconv . Itoa ( options . MaxResults ) )
2019-05-21 05:46:20 +02:00
}
if options . Expand != "" {
2020-05-02 23:14:19 +02:00
uv . Add ( "expand" , options . Expand )
2019-05-21 05:46:20 +02:00
}
if strings . Join ( options . Fields , "," ) != "" {
2020-05-02 23:14:19 +02:00
uv . Add ( "fields" , strings . Join ( options . Fields , "," ) )
2019-05-21 05:46:20 +02:00
}
if options . ValidateQuery != "" {
2020-05-02 23:14:19 +02:00
uv . Add ( "validateQuery" , options . ValidateQuery )
2019-05-21 05:46:20 +02:00
}
2016-06-16 11:10:30 +02:00
}
2020-05-02 23:14:19 +02:00
u . RawQuery = uv . Encode ( )
2020-05-03 15:38:32 +02:00
req , err := s . client . NewRequestWithContext ( ctx , "GET" , u . String ( ) , nil )
2016-05-29 17:30:45 +02:00
if err != nil {
2021-08-27 20:49:33 +02:00
return [ ] Issue { } , map [ string ] string { } , nil , err
2016-05-29 17:30:45 +02:00
}
2016-06-04 02:51:44 +02:00
2016-06-04 10:41:34 +02:00
v := new ( searchResult )
resp , err := s . client . Do ( req , v )
2017-11-04 00:22:32 +02:00
if err != nil {
err = NewJiraError ( resp , err )
}
2021-08-27 20:49:33 +02:00
return v . Issues , v . Names , resp , err
2016-06-04 10:41:34 +02:00
}
2016-06-04 02:51:44 +02:00
2020-05-03 15:38:32 +02:00
// Search wraps SearchWithContext using the background context.
2021-08-27 20:49:33 +02:00
func ( s * IssueService ) Search ( jql string , options * SearchOptions ) ( [ ] Issue , map [ string ] string , * Response , error ) {
2020-05-03 15:38:32 +02:00
return s . SearchWithContext ( context . Background ( ) , jql , options )
}
2021-10-25 12:04:28 +02:00
// Search wraps SearchWithContext using the background context.
func ( s * IssueService ) SearchBoard ( boardId int , options * SearchOptions ) ( [ ] Issue , map [ string ] string , * Response , error ) {
return s . SearchBoardWithContext ( context . Background ( ) , boardId , options )
}
func ( s * IssueService ) SearchBoardWithContext ( ctx context . Context , boardId int , options * SearchOptions ) ( [ ] Issue , map [ string ] string , * Response , error ) {
u := url . URL {
Path : fmt . Sprintf ( "rest/agile/1.0/board/%d/issue" , boardId ) ,
}
uv := url . Values { }
if options != nil {
if options . StartAt != 0 {
uv . Add ( "startAt" , strconv . Itoa ( options . StartAt ) )
}
if options . MaxResults != 0 {
uv . Add ( "maxResults" , strconv . Itoa ( options . MaxResults ) )
}
if options . Expand != "" {
uv . Add ( "expand" , options . Expand )
}
if strings . Join ( options . Fields , "," ) != "" {
uv . Add ( "fields" , strings . Join ( options . Fields , "," ) )
}
if options . ValidateQuery != "" {
uv . Add ( "validateQuery" , options . ValidateQuery )
}
}
u . RawQuery = uv . Encode ( )
req , err := s . client . NewRequestWithContext ( ctx , "GET" , u . String ( ) , nil )
if err != nil {
return [ ] Issue { } , map [ string ] string { } , nil , err
}
v := new ( searchResult )
resp , err := s . client . Do ( req , v )
if err != nil {
err = NewJiraError ( resp , err )
}
return v . Issues , v . Names , resp , err
}
2020-05-03 15:38:32 +02:00
// SearchPagesWithContext will get issues from all pages in a search
2017-11-01 18:19:19 +02:00
//
2020-05-14 17:18:31 +02:00
// Jira API docs: https://developer.atlassian.com/jiradev/jira-apis/jira-rest-apis/jira-rest-api-tutorials/jira-rest-api-example-query-issues
2020-05-03 15:38:32 +02:00
func ( s * IssueService ) SearchPagesWithContext ( ctx context . Context , jql string , options * SearchOptions , f func ( Issue ) error ) error {
2017-11-01 18:19:19 +02:00
if options == nil {
options = & SearchOptions {
StartAt : 0 ,
MaxResults : 50 ,
}
}
if options . MaxResults == 0 {
options . MaxResults = 50
}
2021-08-27 20:49:33 +02:00
issues , _ , resp , err := s . SearchWithContext ( ctx , jql , options )
2017-11-01 18:19:19 +02:00
if err != nil {
return err
}
2020-03-05 15:57:30 +02:00
if len ( issues ) == 0 {
return nil
}
2017-11-01 18:19:19 +02:00
for {
for _ , issue := range issues {
err = f ( issue )
if err != nil {
return err
}
}
if resp . StartAt + resp . MaxResults >= resp . Total {
return nil
}
options . StartAt += resp . MaxResults
2021-08-27 20:49:33 +02:00
issues , _ , resp , err = s . SearchWithContext ( ctx , jql , options )
2017-11-01 18:19:19 +02:00
if err != nil {
return err
}
}
}
2020-05-03 15:38:32 +02:00
// 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 ) {
2016-06-04 02:51:44 +02:00
apiEndpoint := fmt . Sprintf ( "rest/api/2/issue/%s" , issueID )
2020-05-03 15:38:32 +02:00
req , err := s . client . NewRequestWithContext ( ctx , "GET" , apiEndpoint , nil )
2016-06-04 02:51:44 +02:00
if err != nil {
return nil , nil , err
}
issue := new ( map [ string ] interface { } )
resp , err := s . client . Do ( req , issue )
if err != nil {
2017-11-04 00:22:32 +02:00
jerr := NewJiraError ( resp , err )
return nil , resp , jerr
2016-06-04 02:51:44 +02:00
}
2016-06-04 10:41:34 +02:00
2016-06-04 02:51:44 +02:00
m := * issue
f := m [ "fields" ]
cf := make ( CustomFields )
if f == nil {
return cf , resp , nil
}
if rec , ok := f . ( map [ string ] interface { } ) ; ok {
for key , val := range rec {
if strings . Contains ( key , "customfield" ) {
2016-09-07 01:14:15 +02:00
if valMap , ok := val . ( map [ string ] interface { } ) ; ok {
if v , ok := valMap [ "value" ] ; ok {
val = v
}
}
2016-06-04 02:51:44 +02:00
cf [ key ] = fmt . Sprint ( val )
}
}
}
return cf , resp , nil
2016-05-29 17:30:45 +02:00
}
2016-07-17 11:23:49 +02:00
2020-05-03 15:38:32 +02:00
// 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,
2016-07-17 11:23:49 +02:00
// along with fields that are required and their types.
//
2020-05-14 17:18:31 +02:00
// Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue-getTransitions
2020-05-03 15:38:32 +02:00
func ( s * IssueService ) GetTransitionsWithContext ( ctx context . Context , id string ) ( [ ] Transition , * Response , error ) {
2016-07-17 11:23:49 +02:00
apiEndpoint := fmt . Sprintf ( "rest/api/2/issue/%s/transitions?expand=transitions.fields" , id )
2020-05-03 15:38:32 +02:00
req , err := s . client . NewRequestWithContext ( ctx , "GET" , apiEndpoint , nil )
2016-07-17 11:23:49 +02:00
if err != nil {
return nil , nil , err
}
result := new ( transitionResult )
resp , err := s . client . Do ( req , result )
2017-11-04 00:22:32 +02:00
if err != nil {
err = NewJiraError ( resp , err )
}
2016-07-17 11:23:49 +02:00
return result . Transitions , resp , err
}
2020-05-03 15:38:32 +02:00
// 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.
2016-07-17 11:23:49 +02:00
// When performing the transition you can update or set other issue fields.
//
2020-05-14 17:18:31 +02:00
// Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue-doTransition
2020-05-03 15:38:32 +02:00
func ( s * IssueService ) DoTransitionWithContext ( ctx context . Context , ticketID , transitionID string ) ( * Response , error ) {
2016-07-17 11:23:49 +02:00
payload := CreateTransitionPayload {
Transition : TransitionPayload {
ID : transitionID ,
} ,
}
2020-05-03 15:38:32 +02:00
return s . DoTransitionWithPayloadWithContext ( ctx , ticketID , payload )
2017-05-04 15:45:34 +02:00
}
2020-05-03 15:38:32 +02:00
// 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.
2017-05-04 15:45:34 +02:00
// When performing the transition you can update or set other issue fields.
//
2020-05-14 17:18:31 +02:00
// Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue-doTransition
2020-05-03 15:38:32 +02:00
func ( s * IssueService ) DoTransitionWithPayloadWithContext ( ctx context . Context , ticketID , payload interface { } ) ( * Response , error ) {
2017-05-04 15:45:34 +02:00
apiEndpoint := fmt . Sprintf ( "rest/api/2/issue/%s/transitions" , ticketID )
2020-05-03 15:38:32 +02:00
req , err := s . client . NewRequestWithContext ( ctx , "POST" , apiEndpoint , payload )
2016-07-17 11:23:49 +02:00
if err != nil {
return nil , err
}
resp , err := s . client . Do ( req , nil )
if err != nil {
2017-11-04 00:22:32 +02:00
err = NewJiraError ( resp , err )
2016-07-17 11:23:49 +02:00
}
2017-11-04 00:22:32 +02:00
return resp , err
2016-07-17 11:24:23 +02:00
}
2016-10-05 18:04:48 +02:00
2020-05-03 15:38:32 +02:00
// DoTransitionWithPayload wraps DoTransitionWithPayloadWithContext using the background context.
func ( s * IssueService ) DoTransitionWithPayload ( ticketID , payload interface { } ) ( * Response , error ) {
return s . DoTransitionWithPayloadWithContext ( context . Background ( ) , ticketID , payload )
}
2016-10-05 18:04:48 +02:00
// 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.
// * fieldsConfig is a key->value pair where key represents the name of the field as seen in the UI
2017-04-27 07:06:10 +02:00
// And value is the string value for that particular key.
2016-10-05 18:04:48 +02:00
// Note: This method doesn't verify that the fieldsConfig is complete with mandatory fields. The fieldsConfig is
// supposed to be already verified with MetaIssueType.CheckCompleteAndAvailable. It will however return
// error if the key is not found.
// All values will be packed into Unknowns. This is much convenient. If the struct fields needs to be
// configured as well, marshalling and unmarshalling will set the proper fields.
func InitIssueWithMetaAndFields ( metaProject * MetaProject , metaIssuetype * MetaIssueType , fieldsConfig map [ string ] string ) ( * Issue , error ) {
issue := new ( Issue )
issueFields := new ( IssueFields )
issueFields . Unknowns = tcontainer . NewMarshalMap ( )
// map the field names the User presented to jira's internal key
allFields , _ := metaIssuetype . GetAllFields ( )
for key , value := range fieldsConfig {
jiraKey , found := allFields [ key ]
if ! found {
2018-03-01 08:46:54 +02:00
return nil , fmt . Errorf ( "key %s is not found in the list of fields" , key )
2016-10-05 18:04:48 +02:00
}
valueType , err := metaIssuetype . Fields . String ( jiraKey + "/schema/type" )
if err != nil {
return nil , err
}
switch valueType {
case "array" :
elemType , err := metaIssuetype . Fields . String ( jiraKey + "/schema/items" )
if err != nil {
return nil , err
}
switch elemType {
case "component" :
2017-05-01 15:06:18 +02:00
issueFields . Unknowns [ jiraKey ] = [ ] Component { { Name : value } }
2017-10-27 23:22:58 +02:00
case "option" :
issueFields . Unknowns [ jiraKey ] = [ ] map [ string ] string { { "value" : value } }
2016-10-05 18:04:48 +02:00
default :
issueFields . Unknowns [ jiraKey ] = [ ] string { value }
}
case "string" :
issueFields . Unknowns [ jiraKey ] = value
case "date" :
issueFields . Unknowns [ jiraKey ] = value
2017-07-31 09:03:34 +02:00
case "datetime" :
issueFields . Unknowns [ jiraKey ] = value
2016-10-05 18:04:48 +02:00
case "any" :
// Treat any as string
issueFields . Unknowns [ jiraKey ] = value
case "project" :
issueFields . Unknowns [ jiraKey ] = Project {
Name : metaProject . Name ,
ID : metaProject . Id ,
}
case "priority" :
issueFields . Unknowns [ jiraKey ] = Priority { Name : value }
case "user" :
issueFields . Unknowns [ jiraKey ] = User {
Name : value ,
}
case "issuetype" :
issueFields . Unknowns [ jiraKey ] = IssueType {
Name : value ,
}
2017-04-27 07:06:10 +02:00
case "option" :
issueFields . Unknowns [ jiraKey ] = Option {
Value : value ,
}
2016-10-05 18:04:48 +02:00
default :
style: Fix staticcheck (static analysis) errors for this library (#283)
* style: Fix staticcheck errors for "error strings should not be capitalized (ST1005)"
staticcheck is a static analysis tool for go.
It reports several "error strings should not be capitalized (ST1005)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "printf-style function with dynamic format ... (SA1006)"
staticcheck is a static analysis tool for go.
It reports several "printf-style function with dynamic format string and no further arguments should use print-style function instead (SA1006)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "type X is unused (U1000)"
staticcheck is a static analysis tool for go.
It reports several "type X is unused (U1000)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "should use X instead (S1003 & SA6005)"
staticcheck is a static analysis tool for go.
It reports several
- should use !bytes.Contains(b, []byte(`"password":"bar"`)) instead (S1003)
- should use strings.EqualFold instead (SA6005)
messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "unnecessary use of fmt.Sprintf (S1039)"
staticcheck is a static analysis tool for go.
It report several "unnecessary use of fmt.Sprintf (S1039)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "this value of X is never used (SA4006)"
staticcheck is a static analysis tool for go.
It report several "this value of X is never used (SA4006)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "redundant return statement (S1023)"
staticcheck is a static analysis tool for go.
It report several "redundant return statement (S1023)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "possible nil pointer dereference (SA5011)"
staticcheck is a static analysis tool for go.
It report several
file.go:Line:character: possible nil pointer dereference (SA5011)
file.go:Line:character: this check suggests that the pointer can be nil
messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "this value of X is never used (SA4006)"
staticcheck is a static analysis tool for go.
It report several "this value of X is never used (SA4006)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
2020-05-02 23:08:01 +02:00
return nil , fmt . Errorf ( "unknown issue type encountered: %s for %s" , valueType , key )
2016-10-05 18:04:48 +02:00
}
}
issue . Fields = issueFields
return issue , nil
}
2017-02-24 03:41:52 +02:00
2020-05-03 15:38:32 +02:00
// DeleteWithContext will delete a specified issue.
func ( s * IssueService ) DeleteWithContext ( ctx context . Context , issueID string ) ( * Response , error ) {
2017-02-24 03:41:52 +02:00
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
deletePayload := make ( map [ string ] interface { } )
deletePayload [ "deleteSubtasks" ] = "true"
content , _ := json . Marshal ( deletePayload )
2020-05-03 15:38:32 +02:00
req , err := s . client . NewRequestWithContext ( ctx , "DELETE" , apiEndpoint , content )
2017-02-24 03:41:52 +02:00
if err != nil {
return nil , err
}
resp , err := s . client . Do ( req , nil )
return resp , err
}
2017-12-11 15:16:13 +02:00
2020-05-03 15:38:32 +02:00
// 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
2017-12-18 12:30:59 +02:00
//
2020-05-14 17:18:31 +02:00
// Jira API docs: https://docs.atlassian.com/software/jira/docs/api/REST/latest/#api/2/issue-getIssueWatchers
2020-05-03 15:38:32 +02:00
func ( s * IssueService ) GetWatchersWithContext ( ctx context . Context , issueID string ) ( * [ ] User , * Response , error ) {
2018-03-01 08:46:54 +02:00
watchesAPIEndpoint := fmt . Sprintf ( "rest/api/2/issue/%s/watchers" , issueID )
2017-12-11 15:16:13 +02:00
2020-05-03 15:38:32 +02:00
req , err := s . client . NewRequestWithContext ( ctx , "GET" , watchesAPIEndpoint , nil )
2017-12-11 15:16:13 +02:00
if err != nil {
return nil , nil , err
}
watches := new ( Watches )
resp , err := s . client . Do ( req , watches )
if err != nil {
return nil , nil , NewJiraError ( resp , err )
}
result := [ ] User { }
for _ , watcher := range watches . Watchers {
style: Fix staticcheck (static analysis) errors for this library (#283)
* style: Fix staticcheck errors for "error strings should not be capitalized (ST1005)"
staticcheck is a static analysis tool for go.
It reports several "error strings should not be capitalized (ST1005)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "printf-style function with dynamic format ... (SA1006)"
staticcheck is a static analysis tool for go.
It reports several "printf-style function with dynamic format string and no further arguments should use print-style function instead (SA1006)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "type X is unused (U1000)"
staticcheck is a static analysis tool for go.
It reports several "type X is unused (U1000)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "should use X instead (S1003 & SA6005)"
staticcheck is a static analysis tool for go.
It reports several
- should use !bytes.Contains(b, []byte(`"password":"bar"`)) instead (S1003)
- should use strings.EqualFold instead (SA6005)
messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "unnecessary use of fmt.Sprintf (S1039)"
staticcheck is a static analysis tool for go.
It report several "unnecessary use of fmt.Sprintf (S1039)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "this value of X is never used (SA4006)"
staticcheck is a static analysis tool for go.
It report several "this value of X is never used (SA4006)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "redundant return statement (S1023)"
staticcheck is a static analysis tool for go.
It report several "redundant return statement (S1023)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "possible nil pointer dereference (SA5011)"
staticcheck is a static analysis tool for go.
It report several
file.go:Line:character: possible nil pointer dereference (SA5011)
file.go:Line:character: this check suggests that the pointer can be nil
messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
* style: Fix staticcheck errors for "this value of X is never used (SA4006)"
staticcheck is a static analysis tool for go.
It report several "this value of X is never used (SA4006)" messages.
Here, we fix it to be more compliant with the go coding styleguide.
Related: #280
2020-05-02 23:08:01 +02:00
var user * User
2020-03-10 18:03:59 +02:00
if watcher . AccountID != "" {
user , resp , err = s . client . User . GetByAccountID ( watcher . AccountID )
if err != nil {
return nil , resp , NewJiraError ( resp , err )
}
2017-12-11 15:16:13 +02:00
}
result = append ( result , * user )
}
return & result , resp , nil
}
2020-05-03 15:38:32 +02:00
// 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
2017-12-18 12:30:59 +02:00
//
2020-05-14 17:18:31 +02:00
// Jira API docs: https://docs.atlassian.com/software/jira/docs/api/REST/latest/#api/2/issue-addWatcher
2020-05-03 15:38:32 +02:00
func ( s * IssueService ) AddWatcherWithContext ( ctx context . Context , issueID string , userName string ) ( * Response , error ) {
2017-12-11 15:16:13 +02:00
apiEndPoint := fmt . Sprintf ( "rest/api/2/issue/%s/watchers" , issueID )
2020-05-03 15:38:32 +02:00
req , err := s . client . NewRequestWithContext ( ctx , "POST" , apiEndPoint , userName )
2017-12-11 15:16:13 +02:00
if err != nil {
return nil , err
}
resp , err := s . client . Do ( req , nil )
if err != nil {
err = NewJiraError ( resp , err )
}
return resp , err
}
2020-05-03 15:38:32 +02:00
// 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
2017-12-18 12:30:59 +02:00
//
2020-05-14 17:18:31 +02:00
// Jira API docs: https://docs.atlassian.com/software/jira/docs/api/REST/latest/#api/2/issue-removeWatcher
2020-05-03 15:38:32 +02:00
func ( s * IssueService ) RemoveWatcherWithContext ( ctx context . Context , issueID string , userName string ) ( * Response , error ) {
2017-12-11 15:16:13 +02:00
apiEndPoint := fmt . Sprintf ( "rest/api/2/issue/%s/watchers" , issueID )
2020-05-03 15:38:32 +02:00
req , err := s . client . NewRequestWithContext ( ctx , "DELETE" , apiEndPoint , userName )
2017-12-11 15:16:13 +02:00
if err != nil {
return nil , err
}
resp , err := s . client . Do ( req , nil )
if err != nil {
err = NewJiraError ( resp , err )
}
return resp , err
}
2018-06-28 17:28:06 +02:00
2020-05-03 15:38:32 +02:00
// 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
2018-06-28 17:28:06 +02:00
//
2020-05-14 17:18:31 +02:00
// Jira API docs: https://docs.atlassian.com/software/jira/docs/api/REST/7.10.2/#api/2/issue-assign
2020-05-03 15:38:32 +02:00
func ( s * IssueService ) UpdateAssigneeWithContext ( ctx context . Context , issueID string , assignee * User ) ( * Response , error ) {
2018-06-28 17:28:06 +02:00
apiEndPoint := fmt . Sprintf ( "rest/api/2/issue/%s/assignee" , issueID )
2020-05-03 15:38:32 +02:00
req , err := s . client . NewRequestWithContext ( ctx , "PUT" , apiEndPoint , assignee )
2018-06-28 17:28:06 +02:00
if err != nil {
return nil , err
}
resp , err := s . client . Do ( req , nil )
if err != nil {
err = NewJiraError ( resp , err )
}
return resp , err
}
2018-09-03 21:51:52 +02:00
2020-05-03 15:38:32 +02:00
// UpdateAssignee wraps UpdateAssigneeWithContext using the background context.
func ( s * IssueService ) UpdateAssignee ( issueID string , assignee * User ) ( * Response , error ) {
return s . UpdateAssigneeWithContext ( context . Background ( ) , issueID , assignee )
}
2018-07-21 22:42:58 +02:00
func ( c ChangelogHistory ) CreatedTime ( ) ( time . Time , error ) {
var t time . Time
// Ignore null
if string ( c . Created ) == "null" {
return t , nil
}
t , err := time . Parse ( "2006-01-02T15:04:05.999-0700" , c . Created )
return t , err
}
2019-12-11 15:50:22 +02:00
2020-05-03 15:38:32 +02:00
// GetRemoteLinksWithContext gets remote issue links on the issue.
2019-12-11 15:50:22 +02:00
//
2020-05-14 17:18:31 +02:00
// Jira API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue-getRemoteIssueLinks
2020-05-03 15:38:32 +02:00
func ( s * IssueService ) GetRemoteLinksWithContext ( ctx context . Context , id string ) ( * [ ] RemoteLink , * Response , error ) {
2019-12-11 15:50:22 +02:00
apiEndpoint := fmt . Sprintf ( "rest/api/2/issue/%s/remotelink" , id )
2020-05-03 15:38:32 +02:00
req , err := s . client . NewRequestWithContext ( ctx , "GET" , apiEndpoint , nil )
2019-12-11 15:50:22 +02:00
if err != nil {
return nil , nil , err
}
result := new ( [ ] RemoteLink )
resp , err := s . client . Do ( req , result )
if err != nil {
err = NewJiraError ( resp , err )
}
return result , resp , err
}
2020-03-16 10:10:28 +02:00
2020-05-03 15:38:32 +02:00
// 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.
2020-03-16 10:10:28 +02:00
//
2020-05-14 17:18:31 +02:00
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-issueIdOrKey-remotelink-post
2020-05-03 15:38:32 +02:00
func ( s * IssueService ) AddRemoteLinkWithContext ( ctx context . Context , issueID string , remotelink * RemoteLink ) ( * RemoteLink , * Response , error ) {
2020-03-16 10:10:28 +02:00
apiEndpoint := fmt . Sprintf ( "rest/api/2/issue/%s/remotelink" , issueID )
2020-05-03 15:38:32 +02:00
req , err := s . client . NewRequestWithContext ( ctx , "POST" , apiEndpoint , remotelink )
2020-03-16 10:10:28 +02:00
if err != nil {
return nil , nil , err
}
responseRemotelink := new ( RemoteLink )
resp , err := s . client . Do ( req , responseRemotelink )
if err != nil {
jerr := NewJiraError ( resp , err )
return nil , resp , jerr
}
return responseRemotelink , resp , nil
}
2020-05-03 15:38:32 +02:00
// 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 )
}