1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package gitlab
18
19 import (
20 "encoding/json"
21 "fmt"
22 "net/http"
23 "reflect"
24 "time"
25 )
26
27
28
29
30
31 type IssuesService struct {
32 client *Client
33 timeStats *timeStatsService
34 }
35
36
37 type IssueAuthor struct {
38 ID int `json:"id"`
39 State string `json:"state"`
40 WebURL string `json:"web_url"`
41 Name string `json:"name"`
42 AvatarURL string `json:"avatar_url"`
43 Username string `json:"username"`
44 }
45
46
47 type IssueAssignee struct {
48 ID int `json:"id"`
49 State string `json:"state"`
50 WebURL string `json:"web_url"`
51 Name string `json:"name"`
52 AvatarURL string `json:"avatar_url"`
53 Username string `json:"username"`
54 }
55
56
57 type IssueReferences struct {
58 Short string `json:"short"`
59 Relative string `json:"relative"`
60 Full string `json:"full"`
61 }
62
63
64 type IssueCloser struct {
65 ID int `json:"id"`
66 State string `json:"state"`
67 WebURL string `json:"web_url"`
68 Name string `json:"name"`
69 AvatarURL string `json:"avatar_url"`
70 Username string `json:"username"`
71 }
72
73
74 type IssueLinks struct {
75 Self string `json:"self"`
76 Notes string `json:"notes"`
77 AwardEmoji string `json:"award_emoji"`
78 Project string `json:"project"`
79 }
80
81
82
83
84 type Issue struct {
85 ID int `json:"id"`
86 IID int `json:"iid"`
87 ExternalID string `json:"external_id"`
88 State string `json:"state"`
89 Description string `json:"description"`
90 HealthStatus string `json:"health_status"`
91 Author *IssueAuthor `json:"author"`
92 Milestone *Milestone `json:"milestone"`
93 ProjectID int `json:"project_id"`
94 Assignees []*IssueAssignee `json:"assignees"`
95 Assignee *IssueAssignee `json:"assignee"`
96 UpdatedAt *time.Time `json:"updated_at"`
97 ClosedAt *time.Time `json:"closed_at"`
98 ClosedBy *IssueCloser `json:"closed_by"`
99 Title string `json:"title"`
100 CreatedAt *time.Time `json:"created_at"`
101 MovedToID int `json:"moved_to_id"`
102 Labels Labels `json:"labels"`
103 LabelDetails []*LabelDetails `json:"label_details"`
104 Upvotes int `json:"upvotes"`
105 Downvotes int `json:"downvotes"`
106 DueDate *ISOTime `json:"due_date"`
107 WebURL string `json:"web_url"`
108 References *IssueReferences `json:"references"`
109 TimeStats *TimeStats `json:"time_stats"`
110 Confidential bool `json:"confidential"`
111 Weight int `json:"weight"`
112 DiscussionLocked bool `json:"discussion_locked"`
113 IssueType *string `json:"issue_type,omitempty"`
114 Subscribed bool `json:"subscribed"`
115 UserNotesCount int `json:"user_notes_count"`
116 Links *IssueLinks `json:"_links"`
117 IssueLinkID int `json:"issue_link_id"`
118 MergeRequestCount int `json:"merge_requests_count"`
119 EpicIssueID int `json:"epic_issue_id"`
120 Epic *Epic `json:"epic"`
121 Iteration *GroupIteration `json:"iteration"`
122 TaskCompletionStatus *TasksCompletionStatus `json:"task_completion_status"`
123 }
124
125 func (i Issue) String() string {
126 return Stringify(i)
127 }
128
129
130 func (i *Issue) UnmarshalJSON(data []byte) error {
131 type alias Issue
132
133 raw := make(map[string]interface{})
134 err := json.Unmarshal(data, &raw)
135 if err != nil {
136 return err
137 }
138
139 if reflect.TypeOf(raw["id"]).Kind() == reflect.String {
140 raw["external_id"] = raw["id"]
141 delete(raw, "id")
142 }
143
144 labelDetails, ok := raw["labels"].([]interface{})
145 if ok && len(labelDetails) > 0 {
146
147 if _, ok := labelDetails[0].(map[string]interface{}); ok {
148 labels := make([]interface{}, len(labelDetails))
149 for i, details := range labelDetails {
150 labels[i] = details.(map[string]interface{})["name"]
151 }
152
153
154 raw["labels"] = labels
155 raw["label_details"] = labelDetails
156 }
157 }
158
159 data, err = json.Marshal(raw)
160 if err != nil {
161 return err
162 }
163
164 return json.Unmarshal(data, (*alias)(i))
165 }
166
167
168 type LabelDetails struct {
169 ID int `json:"id"`
170 Name string `json:"name"`
171 Color string `json:"color"`
172 Description string `json:"description"`
173 DescriptionHTML string `json:"description_html"`
174 TextColor string `json:"text_color"`
175 }
176
177
178
179
180 type ListIssuesOptions struct {
181 ListOptions
182 State *string `url:"state,omitempty" json:"state,omitempty"`
183 Labels *LabelOptions `url:"labels,comma,omitempty" json:"labels,omitempty"`
184 NotLabels *LabelOptions `url:"not[labels],comma,omitempty" json:"not[labels],omitempty"`
185 WithLabelDetails *bool `url:"with_labels_details,omitempty" json:"with_labels_details,omitempty"`
186 Milestone *string `url:"milestone,omitempty" json:"milestone,omitempty"`
187 NotMilestone *string `url:"not[milestone],omitempty" json:"not[milestone],omitempty"`
188 Scope *string `url:"scope,omitempty" json:"scope,omitempty"`
189 AuthorID *int `url:"author_id,omitempty" json:"author_id,omitempty"`
190 AuthorUsername *string `url:"author_username,omitempty" json:"author_username,omitempty"`
191 NotAuthorUsername *string `url:"not[author_username],omitempty" json:"not[author_username],omitempty"`
192 NotAuthorID *[]int `url:"not[author_id],omitempty" json:"not[author_id],omitempty"`
193 AssigneeID *AssigneeIDValue `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
194 NotAssigneeID *[]int `url:"not[assignee_id],omitempty" json:"not[assignee_id],omitempty"`
195 AssigneeUsername *string `url:"assignee_username,omitempty" json:"assignee_username,omitempty"`
196 NotAssigneeUsername *string `url:"not[assignee_username],omitempty" json:"not[assignee_username],omitempty"`
197 MyReactionEmoji *string `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
198 NotMyReactionEmoji *[]string `url:"not[my_reaction_emoji],omitempty" json:"not[my_reaction_emoji],omitempty"`
199 IIDs *[]int `url:"iids[],omitempty" json:"iids,omitempty"`
200 In *string `url:"in,omitempty" json:"in,omitempty"`
201 NotIn *string `url:"not[in],omitempty" json:"not[in],omitempty"`
202 OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
203 Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
204 Search *string `url:"search,omitempty" json:"search,omitempty"`
205 NotSearch *string `url:"not[search],omitempty" json:"not[search],omitempty"`
206 CreatedAfter *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
207 CreatedBefore *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
208 DueDate *string `url:"due_date,omitempty" json:"due_date,omitempty"`
209 UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
210 UpdatedBefore *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
211 Confidential *bool `url:"confidential,omitempty" json:"confidential,omitempty"`
212 IssueType *string `url:"issue_type,omitempty" json:"issue_type,omitempty"`
213 IterationID *int `url:"iteration_id,omitempty" json:"iteration_id,omitempty"`
214 }
215
216
217
218
219
220 func (s *IssuesService) ListIssues(opt *ListIssuesOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
221 req, err := s.client.NewRequest(http.MethodGet, "issues", opt, options)
222 if err != nil {
223 return nil, nil, err
224 }
225
226 var i []*Issue
227 resp, err := s.client.Do(req, &i)
228 if err != nil {
229 return nil, resp, err
230 }
231
232 return i, resp, nil
233 }
234
235
236
237
238 type ListGroupIssuesOptions struct {
239 ListOptions
240 State *string `url:"state,omitempty" json:"state,omitempty"`
241 Labels *LabelOptions `url:"labels,comma,omitempty" json:"labels,omitempty"`
242 NotLabels *LabelOptions `url:"not[labels],comma,omitempty" json:"not[labels],omitempty"`
243 WithLabelDetails *bool `url:"with_labels_details,omitempty" json:"with_labels_details,omitempty"`
244 IIDs *[]int `url:"iids[],omitempty" json:"iids,omitempty"`
245 Milestone *string `url:"milestone,omitempty" json:"milestone,omitempty"`
246 NotMilestone *string `url:"not[milestone],omitempty" json:"not[milestone],omitempty"`
247 Scope *string `url:"scope,omitempty" json:"scope,omitempty"`
248 AuthorID *int `url:"author_id,omitempty" json:"author_id,omitempty"`
249 NotAuthorID *[]int `url:"not[author_id],omitempty" json:"not[author_id],omitempty"`
250 AuthorUsername *string `url:"author_username,omitempty" json:"author_username,omitempty"`
251 NotAuthorUsername *string `url:"not[author_username],omitempty" json:"not[author_username],omitempty"`
252
253 AssigneeID *AssigneeIDValue `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
254 NotAssigneeID *[]int `url:"not[assignee_id],omitempty" json:"not[assignee_id],omitempty"`
255 AssigneeUsername *string `url:"assignee_username,omitempty" json:"assignee_username,omitempty"`
256 NotAssigneeUsername *string `url:"not[assignee_username],omitempty" json:"not[assignee_username],omitempty"`
257 MyReactionEmoji *string `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
258 NotMyReactionEmoji *[]string `url:"not[my_reaction_emoji],omitempty" json:"not[my_reaction_emoji],omitempty"`
259 OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
260 Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
261 Search *string `url:"search,omitempty" json:"search,omitempty"`
262 NotSearch *string `url:"not[search],omitempty" json:"not[search],omitempty"`
263 In *string `url:"in,omitempty" json:"in,omitempty"`
264 NotIn *string `url:"not[in],omitempty" json:"not[in],omitempty"`
265 CreatedAfter *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
266 CreatedBefore *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
267 DueDate *string `url:"due_date,omitempty" json:"due_date,omitempty"`
268 UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
269 UpdatedBefore *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
270 IssueType *string `url:"issue_type,omitempty" json:"issue_type,omitempty"`
271 IterationID *int `url:"iteration_id,omitempty" json:"iteration_id,omitempty"`
272 }
273
274
275
276
277
278 func (s *IssuesService) ListGroupIssues(pid interface{}, opt *ListGroupIssuesOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
279 group, err := parseID(pid)
280 if err != nil {
281 return nil, nil, err
282 }
283 u := fmt.Sprintf("groups/%s/issues", PathEscape(group))
284
285 req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
286 if err != nil {
287 return nil, nil, err
288 }
289
290 var i []*Issue
291 resp, err := s.client.Do(req, &i)
292 if err != nil {
293 return nil, resp, err
294 }
295
296 return i, resp, nil
297 }
298
299
300
301
302 type ListProjectIssuesOptions struct {
303 ListOptions
304 IIDs *[]int `url:"iids[],omitempty" json:"iids,omitempty"`
305 State *string `url:"state,omitempty" json:"state,omitempty"`
306 Labels *LabelOptions `url:"labels,comma,omitempty" json:"labels,omitempty"`
307 NotLabels *LabelOptions `url:"not[labels],comma,omitempty" json:"not[labels],omitempty"`
308 WithLabelDetails *bool `url:"with_labels_details,omitempty" json:"with_labels_details,omitempty"`
309 Milestone *string `url:"milestone,omitempty" json:"milestone,omitempty"`
310 NotMilestone *string `url:"not[milestone],omitempty" json:"not[milestone],omitempty"`
311 Scope *string `url:"scope,omitempty" json:"scope,omitempty"`
312 AuthorID *int `url:"author_id,omitempty" json:"author_id,omitempty"`
313 AuthorUsername *string `url:"author_username,omitempty" json:"author_username,omitempty"`
314 NotAuthorUsername *string `url:"not[author_username],omitempty" json:"not[author_username],omitempty"`
315 NotAuthorID *[]int `url:"not[author_id],omitempty" json:"not[author_id],omitempty"`
316 AssigneeID *AssigneeIDValue `url:"assignee_id,omitempty" json:"assignee_id,omitempty"`
317 NotAssigneeID *[]int `url:"not[assignee_id],omitempty" json:"not[assignee_id],omitempty"`
318 AssigneeUsername *string `url:"assignee_username,omitempty" json:"assignee_username,omitempty"`
319 NotAssigneeUsername *string `url:"not[assignee_username],omitempty" json:"not[assignee_username],omitempty"`
320 MyReactionEmoji *string `url:"my_reaction_emoji,omitempty" json:"my_reaction_emoji,omitempty"`
321 NotMyReactionEmoji *[]string `url:"not[my_reaction_emoji],omitempty" json:"not[my_reaction_emoji],omitempty"`
322 OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
323 Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
324 Search *string `url:"search,omitempty" json:"search,omitempty"`
325 In *string `url:"in,omitempty" json:"in,omitempty"`
326 NotIn *string `url:"not[in],omitempty" json:"not[in],omitempty"`
327 CreatedAfter *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
328 CreatedBefore *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
329 DueDate *string `url:"due_date,omitempty" json:"due_date,omitempty"`
330 UpdatedAfter *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
331 UpdatedBefore *time.Time `url:"updated_before,omitempty" json:"updated_before,omitempty"`
332 Confidential *bool `url:"confidential,omitempty" json:"confidential,omitempty"`
333 IssueType *string `url:"issue_type,omitempty" json:"issue_type,omitempty"`
334 IterationID *int `url:"iteration_id,omitempty" json:"iteration_id,omitempty"`
335 }
336
337
338
339
340
341 func (s *IssuesService) ListProjectIssues(pid interface{}, opt *ListProjectIssuesOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
342 project, err := parseID(pid)
343 if err != nil {
344 return nil, nil, err
345 }
346 u := fmt.Sprintf("projects/%s/issues", PathEscape(project))
347
348 req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
349 if err != nil {
350 return nil, nil, err
351 }
352
353 var i []*Issue
354 resp, err := s.client.Do(req, &i)
355 if err != nil {
356 return nil, resp, err
357 }
358
359 return i, resp, nil
360 }
361
362
363
364
365 func (s *IssuesService) GetIssueByID(issue int, options ...RequestOptionFunc) (*Issue, *Response, error) {
366 u := fmt.Sprintf("issues/%d", issue)
367
368 req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
369 if err != nil {
370 return nil, nil, err
371 }
372
373 i := new(Issue)
374 resp, err := s.client.Do(req, i)
375 if err != nil {
376 return nil, resp, err
377 }
378
379 return i, resp, nil
380 }
381
382
383
384
385 func (s *IssuesService) GetIssue(pid interface{}, issue int, options ...RequestOptionFunc) (*Issue, *Response, error) {
386 project, err := parseID(pid)
387 if err != nil {
388 return nil, nil, err
389 }
390 u := fmt.Sprintf("projects/%s/issues/%d", PathEscape(project), issue)
391
392 req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
393 if err != nil {
394 return nil, nil, err
395 }
396
397 i := new(Issue)
398 resp, err := s.client.Do(req, i)
399 if err != nil {
400 return nil, resp, err
401 }
402
403 return i, resp, nil
404 }
405
406
407
408
409 type CreateIssueOptions struct {
410 IID *int `url:"iid,omitempty" json:"iid,omitempty"`
411 Title *string `url:"title,omitempty" json:"title,omitempty"`
412 Description *string `url:"description,omitempty" json:"description,omitempty"`
413 Confidential *bool `url:"confidential,omitempty" json:"confidential,omitempty"`
414 AssigneeIDs *[]int `url:"assignee_ids,omitempty" json:"assignee_ids,omitempty"`
415 MilestoneID *int `url:"milestone_id,omitempty" json:"milestone_id,omitempty"`
416 Labels *LabelOptions `url:"labels,comma,omitempty" json:"labels,omitempty"`
417 CreatedAt *time.Time `url:"created_at,omitempty" json:"created_at,omitempty"`
418 DueDate *ISOTime `url:"due_date,omitempty" json:"due_date,omitempty"`
419 EpicID *int `url:"epic_id,omitempty" json:"epic_id,omitempty"`
420 MergeRequestToResolveDiscussionsOf *int `url:"merge_request_to_resolve_discussions_of,omitempty" json:"merge_request_to_resolve_discussions_of,omitempty"`
421 DiscussionToResolve *string `url:"discussion_to_resolve,omitempty" json:"discussion_to_resolve,omitempty"`
422 Weight *int `url:"weight,omitempty" json:"weight,omitempty"`
423 IssueType *string `url:"issue_type,omitempty" json:"issue_type,omitempty"`
424 }
425
426
427
428
429 func (s *IssuesService) CreateIssue(pid interface{}, opt *CreateIssueOptions, options ...RequestOptionFunc) (*Issue, *Response, error) {
430 project, err := parseID(pid)
431 if err != nil {
432 return nil, nil, err
433 }
434 u := fmt.Sprintf("projects/%s/issues", PathEscape(project))
435
436 req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
437 if err != nil {
438 return nil, nil, err
439 }
440
441 i := new(Issue)
442 resp, err := s.client.Do(req, i)
443 if err != nil {
444 return nil, resp, err
445 }
446
447 return i, resp, nil
448 }
449
450
451
452
453 type UpdateIssueOptions struct {
454 Title *string `url:"title,omitempty" json:"title,omitempty"`
455 Description *string `url:"description,omitempty" json:"description,omitempty"`
456 Confidential *bool `url:"confidential,omitempty" json:"confidential,omitempty"`
457 AssigneeIDs *[]int `url:"assignee_ids,omitempty" json:"assignee_ids,omitempty"`
458 MilestoneID *int `url:"milestone_id,omitempty" json:"milestone_id,omitempty"`
459 Labels *LabelOptions `url:"labels,comma,omitempty" json:"labels,omitempty"`
460 AddLabels *LabelOptions `url:"add_labels,comma,omitempty" json:"add_labels,omitempty"`
461 RemoveLabels *LabelOptions `url:"remove_labels,comma,omitempty" json:"remove_labels,omitempty"`
462 StateEvent *string `url:"state_event,omitempty" json:"state_event,omitempty"`
463 UpdatedAt *time.Time `url:"updated_at,omitempty" json:"updated_at,omitempty"`
464 DueDate *ISOTime `url:"due_date,omitempty" json:"due_date,omitempty"`
465 EpicID *int `url:"epic_id,omitempty" json:"epic_id,omitempty"`
466 Weight *int `url:"weight,omitempty" json:"weight,omitempty"`
467 DiscussionLocked *bool `url:"discussion_locked,omitempty" json:"discussion_locked,omitempty"`
468 IssueType *string `url:"issue_type,omitempty" json:"issue_type,omitempty"`
469 }
470
471
472
473
474
475 func (s *IssuesService) UpdateIssue(pid interface{}, issue int, opt *UpdateIssueOptions, options ...RequestOptionFunc) (*Issue, *Response, error) {
476 project, err := parseID(pid)
477 if err != nil {
478 return nil, nil, err
479 }
480 u := fmt.Sprintf("projects/%s/issues/%d", PathEscape(project), issue)
481
482 req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
483 if err != nil {
484 return nil, nil, err
485 }
486
487 i := new(Issue)
488 resp, err := s.client.Do(req, i)
489 if err != nil {
490 return nil, resp, err
491 }
492
493 return i, resp, nil
494 }
495
496
497
498
499 func (s *IssuesService) DeleteIssue(pid interface{}, issue int, options ...RequestOptionFunc) (*Response, error) {
500 project, err := parseID(pid)
501 if err != nil {
502 return nil, err
503 }
504 u := fmt.Sprintf("projects/%s/issues/%d", PathEscape(project), issue)
505
506 req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
507 if err != nil {
508 return nil, err
509 }
510
511 return s.client.Do(req, nil)
512 }
513
514
515
516
517 type ReorderIssueOptions struct {
518 MoveAfterID *int `url:"move_after_id,omitempty" json:"move_after_id,omitempty"`
519 MoveBeforeID *int `url:"move_before_id,omitempty" json:"move_before_id,omitempty"`
520 }
521
522
523
524
525 func (s *IssuesService) ReorderIssue(pid interface{}, issue int, opt *ReorderIssueOptions, options ...RequestOptionFunc) (*Issue, *Response, error) {
526 project, err := parseID(pid)
527 if err != nil {
528 return nil, nil, err
529 }
530 u := fmt.Sprintf("projects/%s/issues/%d/reorder", PathEscape(project), issue)
531
532 req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
533 if err != nil {
534 return nil, nil, err
535 }
536
537 i := new(Issue)
538 resp, err := s.client.Do(req, i)
539 if err != nil {
540 return nil, resp, err
541 }
542
543 return i, resp, nil
544 }
545
546
547
548
549 type MoveIssueOptions struct {
550 ToProjectID *int `url:"to_project_id,omitempty" json:"to_project_id,omitempty"`
551 }
552
553
554
555
556
557 func (s *IssuesService) MoveIssue(pid interface{}, issue int, opt *MoveIssueOptions, options ...RequestOptionFunc) (*Issue, *Response, error) {
558 project, err := parseID(pid)
559 if err != nil {
560 return nil, nil, err
561 }
562 u := fmt.Sprintf("projects/%s/issues/%d/move", PathEscape(project), issue)
563
564 req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
565 if err != nil {
566 return nil, nil, err
567 }
568
569 i := new(Issue)
570 resp, err := s.client.Do(req, i)
571 if err != nil {
572 return nil, resp, err
573 }
574
575 return i, resp, nil
576 }
577
578
579
580
581
582
583
584 func (s *IssuesService) SubscribeToIssue(pid interface{}, issue int, options ...RequestOptionFunc) (*Issue, *Response, error) {
585 project, err := parseID(pid)
586 if err != nil {
587 return nil, nil, err
588 }
589 u := fmt.Sprintf("projects/%s/issues/%d/subscribe", PathEscape(project), issue)
590
591 req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
592 if err != nil {
593 return nil, nil, err
594 }
595
596 i := new(Issue)
597 resp, err := s.client.Do(req, i)
598 if err != nil {
599 return nil, resp, err
600 }
601
602 return i, resp, nil
603 }
604
605
606
607
608
609
610
611 func (s *IssuesService) UnsubscribeFromIssue(pid interface{}, issue int, options ...RequestOptionFunc) (*Issue, *Response, error) {
612 project, err := parseID(pid)
613 if err != nil {
614 return nil, nil, err
615 }
616 u := fmt.Sprintf("projects/%s/issues/%d/unsubscribe", PathEscape(project), issue)
617
618 req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
619 if err != nil {
620 return nil, nil, err
621 }
622
623 i := new(Issue)
624 resp, err := s.client.Do(req, i)
625 if err != nil {
626 return nil, resp, err
627 }
628
629 return i, resp, nil
630 }
631
632
633
634
635
636
637
638 func (s *IssuesService) CreateTodo(pid interface{}, issue int, options ...RequestOptionFunc) (*Todo, *Response, error) {
639 project, err := parseID(pid)
640 if err != nil {
641 return nil, nil, err
642 }
643 u := fmt.Sprintf("projects/%s/issues/%d/todo", PathEscape(project), issue)
644
645 req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
646 if err != nil {
647 return nil, nil, err
648 }
649
650 t := new(Todo)
651 resp, err := s.client.Do(req, t)
652 if err != nil {
653 return nil, resp, err
654 }
655
656 return t, resp, nil
657 }
658
659
660
661
662
663
664 type ListMergeRequestsClosingIssueOptions ListOptions
665
666
667
668
669
670
671 func (s *IssuesService) ListMergeRequestsClosingIssue(pid interface{}, issue int, opt *ListMergeRequestsClosingIssueOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) {
672 project, err := parseID(pid)
673 if err != nil {
674 return nil, nil, err
675 }
676 u := fmt.Sprintf("projects/%s/issues/%d/closed_by", PathEscape(project), issue)
677
678 req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
679 if err != nil {
680 return nil, nil, err
681 }
682
683 var m []*MergeRequest
684 resp, err := s.client.Do(req, &m)
685 if err != nil {
686 return nil, resp, err
687 }
688
689 return m, resp, nil
690 }
691
692
693
694
695
696
697 type ListMergeRequestsRelatedToIssueOptions ListOptions
698
699
700
701
702
703
704 func (s *IssuesService) ListMergeRequestsRelatedToIssue(pid interface{}, issue int, opt *ListMergeRequestsRelatedToIssueOptions, options ...RequestOptionFunc) ([]*MergeRequest, *Response, error) {
705 project, err := parseID(pid)
706 if err != nil {
707 return nil, nil, err
708 }
709 u := fmt.Sprintf("projects/%s/issues/%d/related_merge_requests",
710 PathEscape(project),
711 issue,
712 )
713
714 req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
715 if err != nil {
716 return nil, nil, err
717 }
718
719 var m []*MergeRequest
720 resp, err := s.client.Do(req, &m)
721 if err != nil {
722 return nil, resp, err
723 }
724
725 return m, resp, nil
726 }
727
728
729
730
731
732 func (s *IssuesService) SetTimeEstimate(pid interface{}, issue int, opt *SetTimeEstimateOptions, options ...RequestOptionFunc) (*TimeStats, *Response, error) {
733 return s.timeStats.setTimeEstimate(pid, "issues", issue, opt, options...)
734 }
735
736
737
738
739
740 func (s *IssuesService) ResetTimeEstimate(pid interface{}, issue int, options ...RequestOptionFunc) (*TimeStats, *Response, error) {
741 return s.timeStats.resetTimeEstimate(pid, "issues", issue, options...)
742 }
743
744
745
746
747
748 func (s *IssuesService) AddSpentTime(pid interface{}, issue int, opt *AddSpentTimeOptions, options ...RequestOptionFunc) (*TimeStats, *Response, error) {
749 return s.timeStats.addSpentTime(pid, "issues", issue, opt, options...)
750 }
751
752
753
754
755
756 func (s *IssuesService) ResetSpentTime(pid interface{}, issue int, options ...RequestOptionFunc) (*TimeStats, *Response, error) {
757 return s.timeStats.resetSpentTime(pid, "issues", issue, options...)
758 }
759
760
761
762
763
764 func (s *IssuesService) GetTimeSpent(pid interface{}, issue int, options ...RequestOptionFunc) (*TimeStats, *Response, error) {
765 return s.timeStats.getTimeSpent(pid, "issues", issue, options...)
766 }
767
768
769
770
771
772 func (s *IssuesService) GetParticipants(pid interface{}, issue int, options ...RequestOptionFunc) ([]*BasicUser, *Response, error) {
773 project, err := parseID(pid)
774 if err != nil {
775 return nil, nil, err
776 }
777 u := fmt.Sprintf("projects/%s/issues/%d/participants", PathEscape(project), issue)
778
779 req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
780 if err != nil {
781 return nil, nil, err
782 }
783
784 var bu []*BasicUser
785 resp, err := s.client.Do(req, &bu)
786 if err != nil {
787 return nil, resp, err
788 }
789
790 return bu, resp, nil
791 }
792
View as plain text