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}
159
160// CreateIssueNote creates a new note to a single project issue.
161//
162// GitLab API docs:
163// https://docs.gitlab.com/ce/api/notes.html#create-new-issue-note
164func (s *NotesService) CreateIssueNote(pid interface{}, issue int, opt *CreateIssueNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
165 project, err := parseID(pid)
166 if err != nil {
167 return nil, nil, err
168 }
169 u := fmt.Sprintf("projects/%s/issues/%d/notes", pathEscape(project), issue)
170
171 req, err := s.client.NewRequest("POST", u, opt, options)
172 if err != nil {
173 return nil, nil, err
174 }
175
176 n := new(Note)
177 resp, err := s.client.Do(req, n)
178 if err != nil {
179 return nil, resp, err
180 }
181
182 return n, resp, err
183}
184
185// UpdateIssueNoteOptions represents the available UpdateIssueNote()
186// options.
187//
188// GitLab API docs:
189// https://docs.gitlab.com/ce/api/notes.html#modify-existing-issue-note
190type UpdateIssueNoteOptions struct {
191 Body *string `url:"body,omitempty" json:"body,omitempty"`
192}
193
194// UpdateIssueNote modifies existing note of an issue.
195//
196// GitLab API docs:
197// https://docs.gitlab.com/ce/api/notes.html#modify-existing-issue-note
198func (s *NotesService) UpdateIssueNote(pid interface{}, issue, note int, opt *UpdateIssueNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
199 project, err := parseID(pid)
200 if err != nil {
201 return nil, nil, err
202 }
203 u := fmt.Sprintf("projects/%s/issues/%d/notes/%d", pathEscape(project), issue, note)
204
205 req, err := s.client.NewRequest("PUT", u, opt, options)
206 if err != nil {
207 return nil, nil, err
208 }
209
210 n := new(Note)
211 resp, err := s.client.Do(req, n)
212 if err != nil {
213 return nil, resp, err
214 }
215
216 return n, resp, err
217}
218
219// DeleteIssueNote deletes an existing note of an issue.
220//
221// GitLab API docs:
222// https://docs.gitlab.com/ce/api/notes.html#delete-an-issue-note
223func (s *NotesService) DeleteIssueNote(pid interface{}, issue, note int, options ...OptionFunc) (*Response, error) {
224 project, err := parseID(pid)
225 if err != nil {
226 return nil, err
227 }
228 u := fmt.Sprintf("projects/%s/issues/%d/notes/%d", pathEscape(project), issue, note)
229
230 req, err := s.client.NewRequest("DELETE", u, nil, options)
231 if err != nil {
232 return nil, err
233 }
234
235 return s.client.Do(req, nil)
236}
237
238// ListSnippetNotesOptions represents the available ListSnippetNotes() options.
239//
240// GitLab API docs:
241// https://docs.gitlab.com/ce/api/notes.html#list-all-snippet-notes
242type ListSnippetNotesOptions struct {
243 ListOptions
244 OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
245 Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
246}
247
248// ListSnippetNotes gets a list of all notes for a single snippet. Snippet
249// notes are comments users can post to a snippet.
250//
251// GitLab API docs:
252// https://docs.gitlab.com/ce/api/notes.html#list-all-snippet-notes
253func (s *NotesService) ListSnippetNotes(pid interface{}, snippet int, opt *ListSnippetNotesOptions, options ...OptionFunc) ([]*Note, *Response, error) {
254 project, err := parseID(pid)
255 if err != nil {
256 return nil, nil, err
257 }
258 u := fmt.Sprintf("projects/%s/snippets/%d/notes", pathEscape(project), snippet)
259
260 req, err := s.client.NewRequest("GET", u, opt, options)
261 if err != nil {
262 return nil, nil, err
263 }
264
265 var n []*Note
266 resp, err := s.client.Do(req, &n)
267 if err != nil {
268 return nil, resp, err
269 }
270
271 return n, resp, err
272}
273
274// GetSnippetNote returns a single note for a given snippet.
275//
276// GitLab API docs:
277// https://docs.gitlab.com/ce/api/notes.html#get-single-snippet-note
278func (s *NotesService) GetSnippetNote(pid interface{}, snippet, note int, options ...OptionFunc) (*Note, *Response, error) {
279 project, err := parseID(pid)
280 if err != nil {
281 return nil, nil, err
282 }
283 u := fmt.Sprintf("projects/%s/snippets/%d/notes/%d", pathEscape(project), snippet, note)
284
285 req, err := s.client.NewRequest("GET", u, nil, options)
286 if err != nil {
287 return nil, nil, err
288 }
289
290 n := new(Note)
291 resp, err := s.client.Do(req, n)
292 if err != nil {
293 return nil, resp, err
294 }
295
296 return n, resp, err
297}
298
299// CreateSnippetNoteOptions represents the available CreateSnippetNote()
300// options.
301//
302// GitLab API docs:
303// https://docs.gitlab.com/ce/api/notes.html#create-new-snippet-note
304type CreateSnippetNoteOptions struct {
305 Body *string `url:"body,omitempty" json:"body,omitempty"`
306}
307
308// CreateSnippetNote creates a new note for a single snippet. Snippet notes are
309// comments users can post to a snippet.
310//
311// GitLab API docs:
312// https://docs.gitlab.com/ce/api/notes.html#create-new-snippet-note
313func (s *NotesService) CreateSnippetNote(pid interface{}, snippet int, opt *CreateSnippetNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
314 project, err := parseID(pid)
315 if err != nil {
316 return nil, nil, err
317 }
318 u := fmt.Sprintf("projects/%s/snippets/%d/notes", pathEscape(project), snippet)
319
320 req, err := s.client.NewRequest("POST", u, opt, options)
321 if err != nil {
322 return nil, nil, err
323 }
324
325 n := new(Note)
326 resp, err := s.client.Do(req, n)
327 if err != nil {
328 return nil, resp, err
329 }
330
331 return n, resp, err
332}
333
334// UpdateSnippetNoteOptions represents the available UpdateSnippetNote()
335// options.
336//
337// GitLab API docs:
338// https://docs.gitlab.com/ce/api/notes.html#modify-existing-snippet-note
339type UpdateSnippetNoteOptions struct {
340 Body *string `url:"body,omitempty" json:"body,omitempty"`
341}
342
343// UpdateSnippetNote modifies existing note of a snippet.
344//
345// GitLab API docs:
346// https://docs.gitlab.com/ce/api/notes.html#modify-existing-snippet-note
347func (s *NotesService) UpdateSnippetNote(pid interface{}, snippet, note int, opt *UpdateSnippetNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
348 project, err := parseID(pid)
349 if err != nil {
350 return nil, nil, err
351 }
352 u := fmt.Sprintf("projects/%s/snippets/%d/notes/%d", pathEscape(project), snippet, note)
353
354 req, err := s.client.NewRequest("PUT", u, opt, options)
355 if err != nil {
356 return nil, nil, err
357 }
358
359 n := new(Note)
360 resp, err := s.client.Do(req, n)
361 if err != nil {
362 return nil, resp, err
363 }
364
365 return n, resp, err
366}
367
368// DeleteSnippetNote deletes an existing note of a snippet.
369//
370// GitLab API docs:
371// https://docs.gitlab.com/ce/api/notes.html#delete-a-snippet-note
372func (s *NotesService) DeleteSnippetNote(pid interface{}, snippet, note int, options ...OptionFunc) (*Response, error) {
373 project, err := parseID(pid)
374 if err != nil {
375 return nil, err
376 }
377 u := fmt.Sprintf("projects/%s/snippets/%d/notes/%d", pathEscape(project), snippet, note)
378
379 req, err := s.client.NewRequest("DELETE", u, nil, options)
380 if err != nil {
381 return nil, err
382 }
383
384 return s.client.Do(req, nil)
385}
386
387// ListMergeRequestNotesOptions represents the available ListMergeRequestNotes()
388// options.
389//
390// GitLab API docs:
391// https://docs.gitlab.com/ce/api/notes.html#list-all-merge-request-notes
392type ListMergeRequestNotesOptions struct {
393 ListOptions
394 OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
395 Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
396}
397
398// ListMergeRequestNotes gets a list of all notes for a single merge request.
399//
400// GitLab API docs:
401// https://docs.gitlab.com/ce/api/notes.html#list-all-merge-request-notes
402func (s *NotesService) ListMergeRequestNotes(pid interface{}, mergeRequest int, opt *ListMergeRequestNotesOptions, options ...OptionFunc) ([]*Note, *Response, error) {
403 project, err := parseID(pid)
404 if err != nil {
405 return nil, nil, err
406 }
407 u := fmt.Sprintf("projects/%s/merge_requests/%d/notes", pathEscape(project), mergeRequest)
408
409 req, err := s.client.NewRequest("GET", u, opt, options)
410 if err != nil {
411 return nil, nil, err
412 }
413
414 var n []*Note
415 resp, err := s.client.Do(req, &n)
416 if err != nil {
417 return nil, resp, err
418 }
419
420 return n, resp, err
421}
422
423// GetMergeRequestNote returns a single note for a given merge request.
424//
425// GitLab API docs:
426// https://docs.gitlab.com/ce/api/notes.html#get-single-merge-request-note
427func (s *NotesService) GetMergeRequestNote(pid interface{}, mergeRequest, note int, options ...OptionFunc) (*Note, *Response, error) {
428 project, err := parseID(pid)
429 if err != nil {
430 return nil, nil, err
431 }
432 u := fmt.Sprintf("projects/%s/merge_requests/%d/notes/%d", pathEscape(project), mergeRequest, note)
433
434 req, err := s.client.NewRequest("GET", u, nil, options)
435 if err != nil {
436 return nil, nil, err
437 }
438
439 n := new(Note)
440 resp, err := s.client.Do(req, n)
441 if err != nil {
442 return nil, resp, err
443 }
444
445 return n, resp, err
446}
447
448// CreateMergeRequestNoteOptions represents the available
449// CreateMergeRequestNote() options.
450//
451// GitLab API docs:
452// https://docs.gitlab.com/ce/api/notes.html#create-new-merge-request-note
453type CreateMergeRequestNoteOptions struct {
454 Body *string `url:"body,omitempty" json:"body,omitempty"`
455}
456
457// CreateMergeRequestNote creates a new note for a single merge request.
458//
459// GitLab API docs:
460// https://docs.gitlab.com/ce/api/notes.html#create-new-merge-request-note
461func (s *NotesService) CreateMergeRequestNote(pid interface{}, mergeRequest int, opt *CreateMergeRequestNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
462 project, err := parseID(pid)
463 if err != nil {
464 return nil, nil, err
465 }
466 u := fmt.Sprintf("projects/%s/merge_requests/%d/notes", pathEscape(project), mergeRequest)
467
468 req, err := s.client.NewRequest("POST", u, opt, options)
469 if err != nil {
470 return nil, nil, err
471 }
472
473 n := new(Note)
474 resp, err := s.client.Do(req, n)
475 if err != nil {
476 return nil, resp, err
477 }
478
479 return n, resp, err
480}
481
482// UpdateMergeRequestNoteOptions represents the available
483// UpdateMergeRequestNote() options.
484//
485// GitLab API docs:
486// https://docs.gitlab.com/ce/api/notes.html#modify-existing-merge-request-note
487type UpdateMergeRequestNoteOptions struct {
488 Body *string `url:"body,omitempty" json:"body,omitempty"`
489}
490
491// UpdateMergeRequestNote modifies existing note of a merge request.
492//
493// GitLab API docs:
494// https://docs.gitlab.com/ce/api/notes.html#modify-existing-merge-request-note
495func (s *NotesService) UpdateMergeRequestNote(pid interface{}, mergeRequest, note int, opt *UpdateMergeRequestNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
496 project, err := parseID(pid)
497 if err != nil {
498 return nil, nil, err
499 }
500 u := fmt.Sprintf(
501 "projects/%s/merge_requests/%d/notes/%d", pathEscape(project), mergeRequest, note)
502 req, err := s.client.NewRequest("PUT", u, opt, options)
503 if err != nil {
504 return nil, nil, err
505 }
506
507 n := new(Note)
508 resp, err := s.client.Do(req, n)
509 if err != nil {
510 return nil, resp, err
511 }
512
513 return n, resp, err
514}
515
516// DeleteMergeRequestNote deletes an existing note of a merge request.
517//
518// GitLab API docs:
519// https://docs.gitlab.com/ce/api/notes.html#delete-a-merge-request-note
520func (s *NotesService) DeleteMergeRequestNote(pid interface{}, mergeRequest, note int, options ...OptionFunc) (*Response, error) {
521 project, err := parseID(pid)
522 if err != nil {
523 return nil, err
524 }
525 u := fmt.Sprintf(
526 "projects/%s/merge_requests/%d/notes/%d", pathEscape(project), mergeRequest, note)
527
528 req, err := s.client.NewRequest("DELETE", u, nil, options)
529 if err != nil {
530 return nil, err
531 }
532
533 return s.client.Do(req, nil)
534}
535
536// ListEpicNotesOptions represents the available ListEpicNotes() options.
537//
538// GitLab API docs:
539// https://docs.gitlab.com/ee/api/notes.html#list-all-epic-notes
540type ListEpicNotesOptions struct {
541 ListOptions
542 OrderBy *string `url:"order_by,omitempty" json:"order_by,omitempty"`
543 Sort *string `url:"sort,omitempty" json:"sort,omitempty"`
544}
545
546// ListEpicNotes gets a list of all notes for a single epic.
547//
548// GitLab API docs:
549// https://docs.gitlab.com/ee/api/notes.html#list-all-epic-notes
550func (s *NotesService) ListEpicNotes(gid interface{}, epic int, opt *ListEpicNotesOptions, options ...OptionFunc) ([]*Note, *Response, error) {
551 group, err := parseID(gid)
552 if err != nil {
553 return nil, nil, err
554 }
555 u := fmt.Sprintf("groups/%s/epics/%d/notes", pathEscape(group), epic)
556
557 req, err := s.client.NewRequest("GET", u, opt, options)
558 if err != nil {
559 return nil, nil, err
560 }
561
562 var n []*Note
563 resp, err := s.client.Do(req, &n)
564 if err != nil {
565 return nil, resp, err
566 }
567
568 return n, resp, err
569}
570
571// GetEpicNote returns a single note for an epic.
572//
573// GitLab API docs:
574// https://docs.gitlab.com/ee/api/notes.html#get-single-epic-note
575func (s *NotesService) GetEpicNote(gid interface{}, epic, note int, options ...OptionFunc) (*Note, *Response, error) {
576 group, err := parseID(gid)
577 if err != nil {
578 return nil, nil, err
579 }
580 u := fmt.Sprintf("groups/%s/epics/%d/notes/%d", pathEscape(group), epic, note)
581
582 req, err := s.client.NewRequest("GET", u, nil, options)
583 if err != nil {
584 return nil, nil, err
585 }
586
587 n := new(Note)
588 resp, err := s.client.Do(req, n)
589 if err != nil {
590 return nil, resp, err
591 }
592
593 return n, resp, err
594}
595
596// CreateEpicNoteOptions represents the available CreateEpicNote() options.
597//
598// GitLab API docs:
599// https://docs.gitlab.com/ee/api/notes.html#create-new-epic-note
600type CreateEpicNoteOptions struct {
601 Body *string `url:"body,omitempty" json:"body,omitempty"`
602}
603
604// CreateEpicNote creates a new note for a single merge request.
605//
606// GitLab API docs:
607// https://docs.gitlab.com/ee/api/notes.html#create-new-epic-note
608func (s *NotesService) CreateEpicNote(gid interface{}, epic int, opt *CreateEpicNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
609 group, err := parseID(gid)
610 if err != nil {
611 return nil, nil, err
612 }
613 u := fmt.Sprintf("groups/%s/epics/%d/notes", pathEscape(group), epic)
614
615 req, err := s.client.NewRequest("POST", u, opt, options)
616 if err != nil {
617 return nil, nil, err
618 }
619
620 n := new(Note)
621 resp, err := s.client.Do(req, n)
622 if err != nil {
623 return nil, resp, err
624 }
625
626 return n, resp, err
627}
628
629// UpdateEpicNoteOptions represents the available UpdateEpicNote() options.
630//
631// GitLab API docs:
632// https://docs.gitlab.com/ee/api/notes.html#modify-existing-epic-note
633type UpdateEpicNoteOptions struct {
634 Body *string `url:"body,omitempty" json:"body,omitempty"`
635}
636
637// UpdateEpicNote modifies existing note of an epic.
638//
639// https://docs.gitlab.com/ee/api/notes.html#modify-existing-epic-note
640func (s *NotesService) UpdateEpicNote(gid interface{}, epic, note int, opt *UpdateEpicNoteOptions, options ...OptionFunc) (*Note, *Response, error) {
641 group, err := parseID(gid)
642 if err != nil {
643 return nil, nil, err
644 }
645 u := fmt.Sprintf("groups/%s/epics/%d/notes/%d", pathEscape(group), epic, note)
646
647 req, err := s.client.NewRequest("PUT", u, opt, options)
648 if err != nil {
649 return nil, nil, err
650 }
651
652 n := new(Note)
653 resp, err := s.client.Do(req, n)
654 if err != nil {
655 return nil, resp, err
656 }
657
658 return n, resp, err
659}
660
661// DeleteEpicNote deletes an existing note of a merge request.
662//
663// https://docs.gitlab.com/ee/api/notes.html#delete-an-epic-note
664func (s *NotesService) DeleteEpicNote(gid interface{}, epic, note int, options ...OptionFunc) (*Response, error) {
665 group, err := parseID(gid)
666 if err != nil {
667 return nil, err
668 }
669 u := fmt.Sprintf("groups/%s/epics/%d/notes/%d", pathEscape(group), epic, note)
670
671 req, err := s.client.NewRequest("DELETE", u, nil, options)
672 if err != nil {
673 return nil, err
674 }
675
676 return s.client.Do(req, nil)
677}