Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitebf9d82

Browse files
authored
feat(api): add support for the Draft notes API (#2728)
* feat(api): add support for the Draft notes API* fix(client): handle empty 204 reponses in PUT requests
1 parent428b4fd commitebf9d82

File tree

8 files changed

+297
-0
lines changed

8 files changed

+297
-0
lines changed

‎docs/api-objects.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ API examples
1919
gl_objects/deploy_tokens
2020
gl_objects/deployments
2121
gl_objects/discussions
22+
gl_objects/draft_notes
2223
gl_objects/environments
2324
gl_objects/events
2425
gl_objects/epics

‎docs/gl_objects/draft_notes.rst

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
.. _draft-notes:
2+
3+
###########
4+
Draft Notes
5+
###########
6+
7+
Draft notes are pending, unpublished comments on merge requests.
8+
They can be either start a discussion, or be associated with an existing discussion as a reply.
9+
They are viewable only by the author until they are published.
10+
11+
Reference
12+
---------
13+
14+
* v4 API:
15+
16+
+:class:`gitlab.v4.objects.ProjectMergeRequestDraftNote`
17+
+:class:`gitlab.v4.objects.ProjectMergeRequestDraftNoteManager`
18+
+:attr:`gitlab.v4.objects.ProjectMergeRequest.draft_notes`
19+
20+
21+
* GitLab API: https://docs.gitlab.com/ee/api/draft_notes.html
22+
23+
Examples
24+
--------
25+
26+
List all draft notes for a merge request::
27+
28+
draft_notes = merge_request.draft_notes.list()
29+
30+
Get a draft note for a merge request by ID::
31+
32+
draft_note = merge_request.draft_notes.get(note_id)
33+
34+
..warning::
35+
36+
When creating or updating draft notes, you can provide a complex nested ``position`` argument as a dictionary.
37+
Please consult the upstream API documentation linked above for the exact up-to-date attributes.
38+
39+
Create a draft note for a merge request::
40+
41+
draft_note = merge_request.draft_notes.create({'note': 'note content'})
42+
43+
Update an existing draft note::
44+
45+
draft_note.note = 'updated note content'
46+
draft_note.save()
47+
48+
Delete an existing draft note::
49+
50+
draft_note.delete()
51+
52+
Publish an existing draft note::
53+
54+
draft_note.publish()
55+
56+
Publish all existing draft notes for a merge request in bulk::
57+
58+
merge_request.draft_notes.bulk_publish()

‎gitlab/client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,6 +1061,8 @@ def http_put(
10611061
raw=raw,
10621062
**kwargs,
10631063
)
1064+
ifresult.status_codeingitlab.const.NO_JSON_RESPONSE_CODES:
1065+
returnresult
10641066
try:
10651067
json_result=result.json()
10661068
ifTYPE_CHECKING:

‎gitlab/v4/objects/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from .deploy_tokensimport*
1919
from .deploymentsimport*
2020
from .discussionsimport*
21+
from .draft_notesimport*
2122
from .environmentsimport*
2223
from .epicsimport*
2324
from .eventsimport*

‎gitlab/v4/objects/draft_notes.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
fromtypingimportAny,cast,Union
2+
3+
fromgitlab.baseimportRESTManager,RESTObject
4+
fromgitlab.mixinsimportCRUDMixin,ObjectDeleteMixin,SaveMixin
5+
fromgitlab.typesimportRequiredOptional
6+
7+
__all__= [
8+
"ProjectMergeRequestDraftNote",
9+
"ProjectMergeRequestDraftNoteManager",
10+
]
11+
12+
13+
classProjectMergeRequestDraftNote(ObjectDeleteMixin,SaveMixin,RESTObject):
14+
defpublish(self,**kwargs:Any)->None:
15+
path=f"{self.manager.path}/{self.encoded_id}/publish"
16+
self.manager.gitlab.http_put(path,**kwargs)
17+
18+
19+
classProjectMergeRequestDraftNoteManager(CRUDMixin,RESTManager):
20+
_path="/projects/{project_id}/merge_requests/{mr_iid}/draft_notes"
21+
_obj_cls=ProjectMergeRequestDraftNote
22+
_from_parent_attrs= {"project_id":"project_id","mr_iid":"iid"}
23+
_create_attrs=RequiredOptional(
24+
required=("note",),
25+
optional=(
26+
"commit_id",
27+
"in_reply_to_discussion_id",
28+
"position",
29+
"resolve_discussion",
30+
),
31+
)
32+
_update_attrs=RequiredOptional(optional=("position",))
33+
34+
defget(
35+
self,id:Union[str,int],lazy:bool=False,**kwargs:Any
36+
)->ProjectMergeRequestDraftNote:
37+
returncast(
38+
ProjectMergeRequestDraftNote,super().get(id=id,lazy=lazy,**kwargs)
39+
)
40+
41+
defbulk_publish(self,**kwargs:Any)->None:
42+
path=f"{self.path}/bulk_publish"
43+
self.gitlab.http_post(path,**kwargs)

‎gitlab/v4/objects/merge_requests.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
from .award_emojisimportProjectMergeRequestAwardEmojiManager# noqa: F401
2929
from .commitsimportProjectCommit,ProjectCommitManager
3030
from .discussionsimportProjectMergeRequestDiscussionManager# noqa: F401
31+
from .draft_notesimportProjectMergeRequestDraftNoteManager
3132
from .eventsimport (# noqa: F401
3233
ProjectMergeRequestResourceLabelEventManager,
3334
ProjectMergeRequestResourceMilestoneEventManager,
@@ -157,6 +158,7 @@ class ProjectMergeRequest(
157158
awardemojis:ProjectMergeRequestAwardEmojiManager
158159
diffs:"ProjectMergeRequestDiffManager"
159160
discussions:ProjectMergeRequestDiscussionManager
161+
draft_notes:ProjectMergeRequestDraftNoteManager
160162
notes:ProjectMergeRequestNoteManager
161163
pipelines:ProjectMergeRequestPipelineManager
162164
resourcelabelevents:ProjectMergeRequestResourceLabelEventManager
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
"""
2+
GitLab API: https://docs.gitlab.com/ee/api/draft_notes.html
3+
"""
4+
fromcopyimportdeepcopy
5+
6+
importpytest
7+
importresponses
8+
9+
fromgitlab.v4.objectsimportProjectMergeRequestDraftNote
10+
11+
draft_note_content= {
12+
"id":1,
13+
"author_id":23,
14+
"merge_request_id":1,
15+
"resolve_discussion":False,
16+
"discussion_id":None,
17+
"note":"Example title",
18+
"commit_id":None,
19+
"line_code":None,
20+
"position": {
21+
"base_sha":None,
22+
"start_sha":None,
23+
"head_sha":None,
24+
"old_path":None,
25+
"new_path":None,
26+
"position_type":"text",
27+
"old_line":None,
28+
"new_line":None,
29+
"line_range":None,
30+
},
31+
}
32+
33+
34+
@pytest.fixture()
35+
defresp_list_merge_request_draft_notes():
36+
withresponses.RequestsMock()asrsps:
37+
rsps.add(
38+
method=responses.GET,
39+
url="http://localhost/api/v4/projects/1/merge_requests/1/draft_notes",
40+
json=[draft_note_content],
41+
content_type="application/json",
42+
status=200,
43+
)
44+
yieldrsps
45+
46+
47+
@pytest.fixture()
48+
defresp_get_merge_request_draft_note():
49+
withresponses.RequestsMock()asrsps:
50+
rsps.add(
51+
method=responses.GET,
52+
url="http://localhost/api/v4/projects/1/merge_requests/1/draft_notes/1",
53+
json=draft_note_content,
54+
content_type="application/json",
55+
status=200,
56+
)
57+
yieldrsps
58+
59+
60+
@pytest.fixture()
61+
defresp_create_merge_request_draft_note():
62+
withresponses.RequestsMock()asrsps:
63+
rsps.add(
64+
method=responses.POST,
65+
url="http://localhost/api/v4/projects/1/merge_requests/1/draft_notes",
66+
json=draft_note_content,
67+
content_type="application/json",
68+
status=201,
69+
)
70+
yieldrsps
71+
72+
73+
@pytest.fixture()
74+
defresp_update_merge_request_draft_note():
75+
updated_content=deepcopy(draft_note_content)
76+
updated_content["note"]="New title"
77+
78+
withresponses.RequestsMock()asrsps:
79+
rsps.add(
80+
method=responses.PUT,
81+
url="http://localhost/api/v4/projects/1/merge_requests/1/draft_notes/1",
82+
json=updated_content,
83+
content_type="application/json",
84+
status=201,
85+
)
86+
yieldrsps
87+
88+
89+
@pytest.fixture()
90+
defresp_delete_merge_request_draft_note():
91+
withresponses.RequestsMock()asrsps:
92+
rsps.add(
93+
method=responses.DELETE,
94+
url="http://localhost/api/v4/projects/1/merge_requests/1/draft_notes/1",
95+
json=draft_note_content,
96+
content_type="application/json",
97+
status=201,
98+
)
99+
yieldrsps
100+
101+
102+
@pytest.fixture()
103+
defresp_publish_merge_request_draft_note():
104+
withresponses.RequestsMock()asrsps:
105+
rsps.add(
106+
method=responses.PUT,
107+
url="http://localhost/api/v4/projects/1/merge_requests/1/draft_notes/1/publish",
108+
status=204,
109+
)
110+
yieldrsps
111+
112+
113+
@pytest.fixture()
114+
defresp_bulk_publish_merge_request_draft_notes():
115+
withresponses.RequestsMock()asrsps:
116+
rsps.add(
117+
method=responses.POST,
118+
url="http://localhost/api/v4/projects/1/merge_requests/1/draft_notes/bulk_publish",
119+
status=204,
120+
)
121+
yieldrsps
122+
123+
124+
deftest_list_merge_requests_draft_notes(
125+
project_merge_request,resp_list_merge_request_draft_notes
126+
):
127+
draft_notes=project_merge_request.draft_notes.list()
128+
assertlen(draft_notes)==1
129+
assertisinstance(draft_notes[0],ProjectMergeRequestDraftNote)
130+
assertdraft_notes[0].note==draft_note_content["note"]
131+
132+
133+
deftest_get_merge_requests_draft_note(
134+
project_merge_request,resp_get_merge_request_draft_note
135+
):
136+
draft_note=project_merge_request.draft_notes.get(1)
137+
assertisinstance(draft_note,ProjectMergeRequestDraftNote)
138+
assertdraft_note.note==draft_note_content["note"]
139+
140+
141+
deftest_create_merge_requests_draft_note(
142+
project_merge_request,resp_create_merge_request_draft_note
143+
):
144+
draft_note=project_merge_request.draft_notes.create({"note":"Example title"})
145+
assertisinstance(draft_note,ProjectMergeRequestDraftNote)
146+
assertdraft_note.note==draft_note_content["note"]
147+
148+
149+
deftest_update_merge_requests_draft_note(
150+
project_merge_request,resp_update_merge_request_draft_note
151+
):
152+
draft_note=project_merge_request.draft_notes.get(1,lazy=True)
153+
draft_note.note="New title"
154+
draft_note.save()
155+
assertdraft_note.note=="New title"
156+
157+
158+
deftest_delete_merge_requests_draft_note(
159+
project_merge_request,resp_delete_merge_request_draft_note
160+
):
161+
draft_note=project_merge_request.draft_notes.get(1,lazy=True)
162+
draft_note.delete()
163+
164+
165+
deftest_publish_merge_requests_draft_note(
166+
project_merge_request,resp_publish_merge_request_draft_note
167+
):
168+
draft_note=project_merge_request.draft_notes.get(1,lazy=True)
169+
draft_note.publish()
170+
171+
172+
deftest_bulk_publish_merge_requests_draft_notes(
173+
project_merge_request,resp_bulk_publish_merge_request_draft_notes
174+
):
175+
project_merge_request.draft_notes.bulk_publish()

‎tests/unit/test_gitlab_http_methods.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,21 @@ def test_put_request_404(gl):
766766
assertresponses.assert_call_count(url,1)isTrue
767767

768768

769+
@responses.activate
770+
deftest_put_request_204(gl):
771+
url="http://localhost/api/v4/projects"
772+
responses.add(
773+
method=responses.PUT,
774+
url=url,
775+
status=204,
776+
match=helpers.MATCH_EMPTY_QUERY_PARAMS,
777+
)
778+
779+
result=gl.http_put("/projects")
780+
assertisinstance(result,requests.Response)
781+
assertresponses.assert_call_count(url,1)isTrue
782+
783+
769784
@responses.activate
770785
deftest_put_request_invalid_data(gl):
771786
url="http://localhost/api/v4/projects"

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp