1//
2// Copyright 2017, Sander van Harmelen
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17package gitlab
18
19import (
20 "fmt"
21 "time"
22)
23
24// NotesService handles communication with the notes related methods
25// of the GitLab API.
26//
27// GitLab API docs: https://docs.gitlab.com/ce/api/notes.html
28type NotesService struct {
29 client *Client
30}
31
32// Note represents a GitLab note.
33//
34// GitLab API docs: https://docs.gitlab.com/ce/api/notes.html
35type Note struct {
36 ID int `json:"id"`
37 Body string `json:"body"`
38 Attachment string `json:"attachment"`
39 Title string `json:"title"`
40 FileName string `json:"file_name"`
41 Author struct {
42 ID int `json:"id"`
43 Username string `json:"username"`
44 Email string `json:"email"`
45 Name string `json:"name"`
46 State string `json:"state"`
47 AvatarURL string `json:"avatar_url"`
48 WebURL string `json:"web_url"`
49 } `json:"author"`
50 System bool `json:"system"`
51 ExpiresAt *time.Time `json:"expires_at"`
52 UpdatedAt *time.Time `json:"updated_at"`
53 CreatedAt *time.Time `json:"created_at"`
54 NoteableID int `json:"noteable_id"`
55 NoteableType string `json:"noteable_type"`
56 Position *NotePosition `json:"position"`
57 Resolvable bool `json:"resolvable"`
58 Resolved bool `json:"resolved"`
59 ResolvedBy struct {
60 ID int `json:"id"`
61 Username string `json:"username"`
62 Email string `json:"email"`
63 Name string `json:"name"`
64 State string `json:"state"`
65 AvatarURL string `json:"avatar_url"`
66 WebURL string `json:"web_url"`
67 } `json:"resolved_by"`
68 NoteableIID int `json:"noteable_iid"`
69}
70
71// NotePosition represents the position attributes of a note.
72type NotePosition struct {
73 BaseSHA string `json:"base_sha"`
74 StartSHA string `json:"start_sha"`
75 HeadSHA string `json:"head_sha"`
76 PositionType string `json:"position_type"`
77 NewPath string `json:"new_path,omitempty"`
78 NewLine int `json:"new_line,omitempty"`
79 OldPath string `json:"old_path,omitempty"`
80 OldLine int `json:"old_line,omitempty"`
81 Width int `json:"width,omitempty"`
82 Height int `json:"height,omitempty"`
83 X int `json:"x,omitempty"`
84 Y int `json:"y,omitempty"`
85}
86
87func (n Note) String() string {
88 return Stringify(n)
89}
90
91// ListIssueNotesOptions represents the available ListIssueNotes() options.
92//
93// GitLab API docs:
94// https://docs.gitlab.com/ce/api/notes.html#list-project-issue-notes
95type ListIssueNotesOptions struct {
96 ListOptions
97 OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
98 Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
99}
100
101// ListIssueNotes gets a list of all notes for a single issue.
102//
103// GitLab API docs:
104// https://docs.gitlab.com/ce/api/notes.html#list-project-issue-notes
105func (s *NotesService) ListIssueNotes(pid interface{}, issue int, opt *ListIssueNotesOptions, options ...OptionFunc) ([]*Note, *Response, error) {
106 project, err := parseID(pid)
107 if err != nil {
108 return nil, nil, err
109 }
110 u := fmt.Sprintf("projects/%s/issues/%d/notes", pathEscape(project), issue)
111
112 req, err := s.client.NewRequest("GET", u, opt, options)
113 if err != nil {
114 return nil, nil, err
115 }
116
117 var n []*Note
118 resp, err := s.client.Do(req, &n)
119 if err != nil {
120 return nil, resp, err
121 }
122
123 return n, resp, err
124}
125
126// GetIssueNote returns a single note for a specific project issue.
127//
128// GitLab API docs:
129// https://docs.gitlab.com/ce/api/notes.html#get-single-issue-note
130func (s *NotesService) GetIssueNote(pid interface{}, issue, note int, options ...OptionFunc) (*Note, *Response, error) {
131 project, err := parseID(pid)
132 if err != nil {
133 return nil, nil, err
134 }
135 u := fmt.Sprintf("projects/%s/issues/%d/notes/%d", pathEscape(project), issue, note)
136
137 req, err := s.client.NewRequest("GET", u, nil, options)
138 if err != nil {
139 return nil, nil, err
140 }
141
142 n := new(Note)
143 resp, err := s.client.Do(req, n)
144 if err != nil {
145 return nil, resp, err
146 }
147
148 return n, resp, err
149}
150
151// CreateIssueNoteOptions represents the available CreateIssueNote()
152// options.
153//
154// GitLab API docs:
155// https://docs.gitlab.com/ce/api/notes.html#create-new-issue-note
156type CreateIssueNoteOptions struct {
157 Body *string `url:"body,omitempty" json:"body,omitempty"`
158 CreatedAt *time.Time `url:"created_at,omitempty" json:"created_at,omitempty"`
159}
160
161// CreateIssueNote creates a new note to a single project issue.
162//
163// GitLab API docs:
164// https://docs.gitlab.com/ce/api/notes.html#create-new-issue-note
165func (s *NotesService) CreateIssueNote(pid interface{}, issue int, opt *CreateIssueNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
166 project, err := parseID(pid)
167 if err != nil {
168 return nil, nil, err
169 }
170 u := fmt.Sprintf("projects/%s/issues/%d/notes", pathEscape(project), issue)
171
172 req, err := s.client.NewRequest("POST", u, opt, options)
173 if err != nil {
174 return nil, nil, err
175 }
176
177 n := new(Note)
178 resp, err := s.client.Do(req, n)
179 if err != nil {
180 return nil, resp, err
181 }
182
183 return n, resp, err
184}
185
186// UpdateIssueNoteOptions represents the available UpdateIssueNote()
187// options.
188//
189// GitLab API docs:
190// https://docs.gitlab.com/ce/api/notes.html#modify-existing-issue-note
191type UpdateIssueNoteOptions struct {
192 Body *string `url:"body,omitempty" json:"body,omitempty"`
193}
194
195// UpdateIssueNote modifies existing note of an issue.
196//
197// GitLab API docs:
198// https://docs.gitlab.com/ce/api/notes.html#modify-existing-issue-note
199func (s *NotesService) UpdateIssueNote(pid interface{}, issue, note int, opt *UpdateIssueNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
200 project, err := parseID(pid)
201 if err != nil {
202 return nil, nil, err
203 }
204 u := fmt.Sprintf("projects/%s/issues/%d/notes/%d", pathEscape(project), issue, note)
205
206 req, err := s.client.NewRequest("PUT", u, opt, options)
207 if err != nil {
208 return nil, nil, err
209 }
210
211 n := new(Note)
212 resp, err := s.client.Do(req, n)
213 if err != nil {
214 return nil, resp, err
215 }
216
217 return n, resp, err
218}
219
220// DeleteIssueNote deletes an existing note of an issue.
221//
222// GitLab API docs:
223// https://docs.gitlab.com/ce/api/notes.html#delete-an-issue-note
224func (s *NotesService) DeleteIssueNote(pid interface{}, issue, note int, options ...OptionFunc) (*Response, error) {
225 project, err := parseID(pid)
226 if err != nil {
227 return nil, err
228 }
229 u := fmt.Sprintf("projects/%s/issues/%d/notes/%d", pathEscape(project), issue, note)
230
231 req, err := s.client.NewRequest("DELETE", u, nil, options)
232 if err != nil {
233 return nil, err
234 }
235
236 return s.client.Do(req, nil)
237}
238
239// ListSnippetNotesOptions represents the available ListSnippetNotes() options.
240//
241// GitLab API docs:
242// https://docs.gitlab.com/ce/api/notes.html#list-all-snippet-notes
243type ListSnippetNotesOptions struct {
244 ListOptions
245 OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
246 Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
247}
248
249// ListSnippetNotes gets a list of all notes for a single snippet. Snippet
250// notes are comments users can post to a snippet.
251//
252// GitLab API docs:
253// https://docs.gitlab.com/ce/api/notes.html#list-all-snippet-notes
254func (s *NotesService) ListSnippetNotes(pid interface{}, snippet int, opt *ListSnippetNotesOptions, options ...OptionFunc) ([]*Note, *Response, error) {
255 project, err := parseID(pid)
256 if err != nil {
257 return nil, nil, err
258 }
259 u := fmt.Sprintf("projects/%s/snippets/%d/notes", pathEscape(project), snippet)
260
261 req, err := s.client.NewRequest("GET", u, opt, options)
262 if err != nil {
263 return nil, nil, err
264 }
265
266 var n []*Note
267 resp, err := s.client.Do(req, &n)
268 if err != nil {
269 return nil, resp, err
270 }
271
272 return n, resp, err
273}
274
275// GetSnippetNote returns a single note for a given snippet.
276//
277// GitLab API docs:
278// https://docs.gitlab.com/ce/api/notes.html#get-single-snippet-note
279func (s *NotesService) GetSnippetNote(pid interface{}, snippet, note int, options ...OptionFunc) (*Note, *Response, error) {
280 project, err := parseID(pid)
281 if err != nil {
282 return nil, nil, err
283 }
284 u := fmt.Sprintf("projects/%s/snippets/%d/notes/%d", pathEscape(project), snippet, note)
285
286 req, err := s.client.NewRequest("GET", u, nil, options)
287 if err != nil {
288 return nil, nil, err
289 }
290
291 n := new(Note)
292 resp, err := s.client.Do(req, n)
293 if err != nil {
294 return nil, resp, err
295 }
296
297 return n, resp, err
298}
299
300// CreateSnippetNoteOptions represents the available CreateSnippetNote()
301// options.
302//
303// GitLab API docs:
304// https://docs.gitlab.com/ce/api/notes.html#create-new-snippet-note
305type CreateSnippetNoteOptions struct {
306 Body *string `url:"body,omitempty" json:"body,omitempty"`
307}
308
309// CreateSnippetNote creates a new note for a single snippet. Snippet notes are
310// comments users can post to a snippet.
311//
312// GitLab API docs:
313// https://docs.gitlab.com/ce/api/notes.html#create-new-snippet-note
314func (s *NotesService) CreateSnippetNote(pid interface{}, snippet int, opt *CreateSnippetNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
315 project, err := parseID(pid)
316 if err != nil {
317 return nil, nil, err
318 }
319 u := fmt.Sprintf("projects/%s/snippets/%d/notes", pathEscape(project), snippet)
320
321 req, err := s.client.NewRequest("POST", u, opt, options)
322 if err != nil {
323 return nil, nil, err
324 }
325
326 n := new(Note)
327 resp, err := s.client.Do(req, n)
328 if err != nil {
329 return nil, resp, err
330 }
331
332 return n, resp, err
333}
334
335// UpdateSnippetNoteOptions represents the available UpdateSnippetNote()
336// options.
337//
338// GitLab API docs:
339// https://docs.gitlab.com/ce/api/notes.html#modify-existing-snippet-note
340type UpdateSnippetNoteOptions struct {
341 Body *string `url:"body,omitempty" json:"body,omitempty"`
342}
343
344// UpdateSnippetNote modifies existing note of a snippet.
345//
346// GitLab API docs:
347// https://docs.gitlab.com/ce/api/notes.html#modify-existing-snippet-note
348func (s *NotesService) UpdateSnippetNote(pid interface{}, snippet, note int, opt *UpdateSnippetNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
349 project, err := parseID(pid)
350 if err != nil {
351 return nil, nil, err
352 }
353 u := fmt.Sprintf("projects/%s/snippets/%d/notes/%d", pathEscape(project), snippet, note)
354
355 req, err := s.client.NewRequest("PUT", u, opt, options)
356 if err != nil {
357 return nil, nil, err
358 }
359
360 n := new(Note)
361 resp, err := s.client.Do(req, n)
362 if err != nil {
363 return nil, resp, err
364 }
365
366 return n, resp, err
367}
368
369// DeleteSnippetNote deletes an existing note of a snippet.
370//
371// GitLab API docs:
372// https://docs.gitlab.com/ce/api/notes.html#delete-a-snippet-note
373func (s *NotesService) DeleteSnippetNote(pid interface{}, snippet, note int, options ...OptionFunc) (*Response, error) {
374 project, err := parseID(pid)
375 if err != nil {
376 return nil, err
377 }
378 u := fmt.Sprintf("projects/%s/snippets/%d/notes/%d", pathEscape(project), snippet, note)
379
380 req, err := s.client.NewRequest("DELETE", u, nil, options)
381 if err != nil {
382 return nil, err
383 }
384
385 return s.client.Do(req, nil)
386}
387
388// ListMergeRequestNotesOptions represents the available ListMergeRequestNotes()
389// options.
390//
391// GitLab API docs:
392// https://docs.gitlab.com/ce/api/notes.html#list-all-merge-request-notes
393type ListMergeRequestNotesOptions struct {
394 ListOptions
395 OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
396 Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
397}
398
399// ListMergeRequestNotes gets a list of all notes for a single merge request.
400//
401// GitLab API docs:
402// https://docs.gitlab.com/ce/api/notes.html#list-all-merge-request-notes
403func (s *NotesService) ListMergeRequestNotes(pid interface{}, mergeRequest int, opt *ListMergeRequestNotesOptions, options ...OptionFunc) ([]*Note, *Response, error) {
404 project, err := parseID(pid)
405 if err != nil {
406 return nil, nil, err
407 }
408 u := fmt.Sprintf("projects/%s/merge_requests/%d/notes", pathEscape(project), mergeRequest)
409
410 req, err := s.client.NewRequest("GET", u, opt, options)
411 if err != nil {
412 return nil, nil, err
413 }
414
415 var n []*Note
416 resp, err := s.client.Do(req, &n)
417 if err != nil {
418 return nil, resp, err
419 }
420
421 return n, resp, err
422}
423
424// GetMergeRequestNote returns a single note for a given merge request.
425//
426// GitLab API docs:
427// https://docs.gitlab.com/ce/api/notes.html#get-single-merge-request-note
428func (s *NotesService) GetMergeRequestNote(pid interface{}, mergeRequest, note int, options ...OptionFunc) (*Note, *Response, error) {
429 project, err := parseID(pid)
430 if err != nil {
431 return nil, nil, err
432 }
433 u := fmt.Sprintf("projects/%s/merge_requests/%d/notes/%d", pathEscape(project), mergeRequest, note)
434
435 req, err := s.client.NewRequest("GET", u, nil, options)
436 if err != nil {
437 return nil, nil, err
438 }
439
440 n := new(Note)
441 resp, err := s.client.Do(req, n)
442 if err != nil {
443 return nil, resp, err
444 }
445
446 return n, resp, err
447}
448
449// CreateMergeRequestNoteOptions represents the available
450// CreateMergeRequestNote() options.
451//
452// GitLab API docs:
453// https://docs.gitlab.com/ce/api/notes.html#create-new-merge-request-note
454type CreateMergeRequestNoteOptions struct {
455 Body *string `url:"body,omitempty" json:"body,omitempty"`
456}
457
458// CreateMergeRequestNote creates a new note for a single merge request.
459//
460// GitLab API docs:
461// https://docs.gitlab.com/ce/api/notes.html#create-new-merge-request-note
462func (s *NotesService) CreateMergeRequestNote(pid interface{}, mergeRequest int, opt *CreateMergeRequestNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
463 project, err := parseID(pid)
464 if err != nil {
465 return nil, nil, err
466 }
467 u := fmt.Sprintf("projects/%s/merge_requests/%d/notes", pathEscape(project), mergeRequest)
468
469 req, err := s.client.NewRequest("POST", u, opt, options)
470 if err != nil {
471 return nil, nil, err
472 }
473
474 n := new(Note)
475 resp, err := s.client.Do(req, n)
476 if err != nil {
477 return nil, resp, err
478 }
479
480 return n, resp, err
481}
482
483// UpdateMergeRequestNoteOptions represents the available
484// UpdateMergeRequestNote() options.
485//
486// GitLab API docs:
487// https://docs.gitlab.com/ce/api/notes.html#modify-existing-merge-request-note
488type UpdateMergeRequestNoteOptions struct {
489 Body *string `url:"body,omitempty" json:"body,omitempty"`
490}
491
492// UpdateMergeRequestNote modifies existing note of a merge request.
493//
494// GitLab API docs:
495// https://docs.gitlab.com/ce/api/notes.html#modify-existing-merge-request-note
496func (s *NotesService) UpdateMergeRequestNote(pid interface{}, mergeRequest, note int, opt *UpdateMergeRequestNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
497 project, err := parseID(pid)
498 if err != nil {
499 return nil, nil, err
500 }
501 u := fmt.Sprintf(
502 "projects/%s/merge_requests/%d/notes/%d", pathEscape(project), mergeRequest, note)
503 req, err := s.client.NewRequest("PUT", u, opt, options)
504 if err != nil {
505 return nil, nil, err
506 }
507
508 n := new(Note)
509 resp, err := s.client.Do(req, n)
510 if err != nil {
511 return nil, resp, err
512 }
513
514 return n, resp, err
515}
516
517// DeleteMergeRequestNote deletes an existing note of a merge request.
518//
519// GitLab API docs:
520// https://docs.gitlab.com/ce/api/notes.html#delete-a-merge-request-note
521func (s *NotesService) DeleteMergeRequestNote(pid interface{}, mergeRequest, note int, options ...OptionFunc) (*Response, error) {
522 project, err := parseID(pid)
523 if err != nil {
524 return nil, err
525 }
526 u := fmt.Sprintf(
527 "projects/%s/merge_requests/%d/notes/%d", pathEscape(project), mergeRequest, note)
528
529 req, err := s.client.NewRequest("DELETE", u, nil, options)
530 if err != nil {
531 return nil, err
532 }
533
534 return s.client.Do(req, nil)
535}
536
537// ListEpicNotesOptions represents the available ListEpicNotes() options.
538//
539// GitLab API docs:
540// https://docs.gitlab.com/ee/api/notes.html#list-all-epic-notes
541type ListEpicNotesOptions struct {
542 ListOptions
543 OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
544 Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
545}
546
547// ListEpicNotes gets a list of all notes for a single epic.
548//
549// GitLab API docs:
550// https://docs.gitlab.com/ee/api/notes.html#list-all-epic-notes
551func (s *NotesService) ListEpicNotes(gid interface{}, epic int, opt *ListEpicNotesOptions, options ...OptionFunc) ([]*Note, *Response, error) {
552 group, err := parseID(gid)
553 if err != nil {
554 return nil, nil, err
555 }
556 u := fmt.Sprintf("groups/%s/epics/%d/notes", pathEscape(group), epic)
557
558 req, err := s.client.NewRequest("GET", u, opt, options)
559 if err != nil {
560 return nil, nil, err
561 }
562
563 var n []*Note
564 resp, err := s.client.Do(req, &n)
565 if err != nil {
566 return nil, resp, err
567 }
568
569 return n, resp, err
570}
571
572// GetEpicNote returns a single note for an epic.
573//
574// GitLab API docs:
575// https://docs.gitlab.com/ee/api/notes.html#get-single-epic-note
576func (s *NotesService) GetEpicNote(gid interface{}, epic, note int, options ...OptionFunc) (*Note, *Response, error) {
577 group, err := parseID(gid)
578 if err != nil {
579 return nil, nil, err
580 }
581 u := fmt.Sprintf("groups/%s/epics/%d/notes/%d", pathEscape(group), epic, note)
582
583 req, err := s.client.NewRequest("GET", u, nil, options)
584 if err != nil {
585 return nil, nil, err
586 }
587
588 n := new(Note)
589 resp, err := s.client.Do(req, n)
590 if err != nil {
591 return nil, resp, err
592 }
593
594 return n, resp, err
595}
596
597// CreateEpicNoteOptions represents the available CreateEpicNote() options.
598//
599// GitLab API docs:
600// https://docs.gitlab.com/ee/api/notes.html#create-new-epic-note
601type CreateEpicNoteOptions struct {
602 Body *string `url:"body,omitempty" json:"body,omitempty"`
603}
604
605// CreateEpicNote creates a new note for a single merge request.
606//
607// GitLab API docs:
608// https://docs.gitlab.com/ee/api/notes.html#create-new-epic-note
609func (s *NotesService) CreateEpicNote(gid interface{}, epic int, opt *CreateEpicNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
610 group, err := parseID(gid)
611 if err != nil {
612 return nil, nil, err
613 }
614 u := fmt.Sprintf("groups/%s/epics/%d/notes", pathEscape(group), epic)
615
616 req, err := s.client.NewRequest("POST", u, opt, options)
617 if err != nil {
618 return nil, nil, err
619 }
620
621 n := new(Note)
622 resp, err := s.client.Do(req, n)
623 if err != nil {
624 return nil, resp, err
625 }
626
627 return n, resp, err
628}
629
630// UpdateEpicNoteOptions represents the available UpdateEpicNote() options.
631//
632// GitLab API docs:
633// https://docs.gitlab.com/ee/api/notes.html#modify-existing-epic-note
634type UpdateEpicNoteOptions struct {
635 Body *string `url:"body,omitempty" json:"body,omitempty"`
636}
637
638// UpdateEpicNote modifies existing note of an epic.
639//
640// https://docs.gitlab.com/ee/api/notes.html#modify-existing-epic-note
641func (s *NotesService) UpdateEpicNote(gid interface{}, epic, note int, opt *UpdateEpicNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
642 group, err := parseID(gid)
643 if err != nil {
644 return nil, nil, err
645 }
646 u := fmt.Sprintf("groups/%s/epics/%d/notes/%d", pathEscape(group), epic, note)
647
648 req, err := s.client.NewRequest("PUT", u, opt, options)
649 if err != nil {
650 return nil, nil, err
651 }
652
653 n := new(Note)
654 resp, err := s.client.Do(req, n)
655 if err != nil {
656 return nil, resp, err
657 }
658
659 return n, resp, err
660}
661
662// DeleteEpicNote deletes an existing note of a merge request.
663//
664// https://docs.gitlab.com/ee/api/notes.html#delete-an-epic-note
665func (s *NotesService) DeleteEpicNote(gid interface{}, epic, note int, options ...OptionFunc) (*Response, error) {
666 group, err := parseID(gid)
667 if err != nil {
668 return nil, err
669 }
670 u := fmt.Sprintf("groups/%s/epics/%d/notes/%d", pathEscape(group), epic, note)
671
672 req, err := s.client.NewRequest("DELETE", u, nil, options)
673 if err != nil {
674 return nil, err
675 }
676
677 return s.client.Do(req, nil)
678}