Skip to content

Commit 20878b8

Browse files
authored
Bitbucket Cloud: Support for pull request tasks (atlassian-api#874)
* This adds support for Pull Request tasks. * Add more test cases * add update testcase * + newline
1 parent 24c3481 commit 20878b8

File tree

6 files changed

+276
-1
lines changed

6 files changed

+276
-1
lines changed

atlassian/bitbucket/cloud/repositories/pullRequests.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,34 @@ def comment(self, raw_message):
253253

254254
return self.post("comments", data)
255255

256+
def tasks(self):
257+
"""
258+
Returns generator object of the tasks endpoint
259+
260+
This is feature currently undocumented.
261+
But confirmed by an Atlassian employee (BCLOUD-16682).
262+
"""
263+
for task in self._get_paged("tasks"):
264+
yield Task(task, **self._new_session_args)
265+
266+
def add_task(self, raw_message):
267+
"""
268+
Adding a task to the pull request in raw format.
269+
270+
This is feature currently undocumented.
271+
But confirmed by an Atlassian employee (BCLOUD-16682).
272+
"""
273+
if not raw_message:
274+
raise ValueError("No message set")
275+
276+
data = {
277+
"content": {
278+
"raw": raw_message,
279+
}
280+
}
281+
282+
return Task(self.post("tasks", data), **self._new_session_args)
283+
256284
def approve(self):
257285
"""
258286
Approve a pull request if open
@@ -483,3 +511,70 @@ def website(self):
483511
def refname(self):
484512
"""Returns the refname"""
485513
return self.get_data("refname")
514+
515+
516+
class Task(BitbucketCloudBase):
517+
STATE_RESOLVED = "RESOLVED"
518+
STATE_UNRESOLVED = "UNRESOLVED"
519+
520+
def __init__(self, data, *args, **kwargs):
521+
super().__init__(None, None, *args, data=data, **kwargs)
522+
523+
@property
524+
def id(self):
525+
"""Task id."""
526+
return self.get_data("id")
527+
528+
@property
529+
def description(self):
530+
"""The task description."""
531+
return self.get_data("content")["raw"]
532+
533+
@property
534+
def created_on(self):
535+
"""time of creation"""
536+
return self.get_time("created_on")
537+
538+
@property
539+
def resolved_on(self):
540+
"""resolve timestamp"""
541+
return self.get_time("resolved_on")
542+
543+
@property
544+
def is_resolved(self):
545+
"""True if the task was already resolved."""
546+
return self.get_data("state") == self.STATE_RESOLVED
547+
548+
@property
549+
def creator(self):
550+
"""User object with user information of the task creator"""
551+
return User(None, self.get_data("creator"), **self._new_session_args)
552+
553+
@property
554+
def resolved_by(self):
555+
"""User object with user information of the task resolver"""
556+
return User(None, self.get_data("resolved_by"), **self._new_session_args)
557+
558+
def update(self, raw_message):
559+
"""
560+
Update a task in raw format
561+
562+
This is feature currently undocumented.
563+
"""
564+
if not raw_message:
565+
raise ValueError("No message set")
566+
567+
data = {
568+
"content": {
569+
"raw": raw_message,
570+
}
571+
}
572+
return self._update_data(self.put(None, data=data))
573+
574+
def delete(self):
575+
"""
576+
Delete the pullrequest tasl.
577+
578+
This is feature currently undocumented.
579+
"""
580+
return super(Task, self).delete(None)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
responses[None]=None
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
responses['{"content": {"raw": "ToDo 10"}}'] = {
2+
"links": {
3+
"self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/tasks/123456"},
4+
"html": {
5+
"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/_/diff#task-123456"
6+
},
7+
},
8+
"creator": {
9+
"display_name": "User04DisplayName",
10+
"uuid": "{User04UUID}",
11+
"links": {
12+
"self": {"href": "users/%7BUser04UUID%7D"},
13+
"html": {"href": "https://bitbucket.org/%7BUser04UUID%7D/"},
14+
"avatar": {
15+
"href": "https://secure.gravatar.com/avatar/ad2424dafaasdssaew12232344434432?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FU4-2.png"
16+
},
17+
},
18+
"nickname": "User04Nickname",
19+
"type": "user",
20+
"account_id": "User04AccountID",
21+
},
22+
"resolved_on": null,
23+
"created_on": "2021-10-19T20:20:49.288763+00:00",
24+
"content": {"raw": "ToDo 10"},
25+
"state": "UNRESOLVED",
26+
"resolved_by": null,
27+
"updated_on": "2021-10-19T20:20:49.288843+00:00",
28+
"id": 123456,
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
responses[None] = {
2+
"pagelen": 10,
3+
"values": [
4+
{
5+
"links": {
6+
"self": {
7+
"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/tasks/234567"
8+
},
9+
"html": {
10+
"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/_/diff#task-234567"
11+
},
12+
},
13+
"creator": {
14+
"display_name": "User04DisplayName",
15+
"uuid": "{User04UUID}",
16+
"links": {
17+
"self": {"href": "users/%7BUser04UUID%7D"},
18+
"html": {"href": "https://bitbucket.org/%7BUser04UUID%7D/"},
19+
"avatar": {
20+
"href": "https://secure.gravatar.com/avatar/ad2424dafaasdssaew12232344434432?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FU4-2.png"
21+
},
22+
},
23+
"nickname": "User04Nickname",
24+
"type": "user",
25+
"account_id": "User04AccountID",
26+
},
27+
"resolved_on": "2021-10-19T20:28:47.493275+00:00",
28+
"created_on": "2021-10-19T20:26:35.443146+00:00",
29+
"content": {"raw": "ToDo 2"},
30+
"state": "RESOLVED",
31+
"resolved_by": {
32+
"display_name": "User04DisplayName",
33+
"uuid": "{User04UUID}",
34+
"links": {
35+
"self": {"href": "users/%7BUser04UUID%7D"},
36+
"html": {"href": "https://bitbucket.org/%7BUser04UUID%7D/"},
37+
"avatar": {
38+
"href": "https://secure.gravatar.com/avatar/ad2424dafaasdssaew12232344434432?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FU4-2.png"
39+
},
40+
},
41+
"nickname": "User04Nickname",
42+
"type": "user",
43+
"account_id": "User04AccountID",
44+
},
45+
"updated_on": "2021-10-19T20:28:47.493283+00:00",
46+
"id": 234567,
47+
},
48+
{
49+
"links": {
50+
"self": {
51+
"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/tasks/123456"
52+
},
53+
"html": {
54+
"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/_/diff#task-123456"
55+
},
56+
},
57+
"creator": {
58+
"display_name": "User04DisplayName",
59+
"uuid": "{User04UUID}",
60+
"links": {
61+
"self": {"href": "users/%7BUser04UUID%7D"},
62+
"html": {"href": "https://bitbucket.org/%7BUser04UUID%7D/"},
63+
"avatar": {
64+
"href": "https://secure.gravatar.com/avatar/ad2424dafaasdssaew12232344434432?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FU4-2.png"
65+
},
66+
},
67+
"nickname": "User04Nickname",
68+
"type": "user",
69+
"account_id": "User04AccountID",
70+
},
71+
"resolved_on": null,
72+
"created_on": "2021-10-19T20:20:49.288763+00:00",
73+
"content": {"raw": "ToDo 1"},
74+
"state": "UNRESOLVED",
75+
"resolved_by": null,
76+
"updated_on": "2021-10-19T20:20:49.288843+00:00",
77+
"id": 123456,
78+
},
79+
],
80+
"page": 1,
81+
"size": 2,
82+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
responses['{"content": {"raw": "ToDo 1"}}'] = {
2+
"links": {
3+
"self": {"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/tasks/123456"},
4+
"html": {
5+
"href": "bitbucket/cloud/2.0/repositories/TestWorkspace1/testrepository1/pullrequests/1/_/diff#task-123456"
6+
},
7+
},
8+
"creator": {
9+
"display_name": "User04DisplayName",
10+
"uuid": "{User04UUID}",
11+
"links": {
12+
"self": {"href": "users/%7BUser04UUID%7D"},
13+
"html": {"href": "https://bitbucket.org/%7BUser04UUID%7D/"},
14+
"avatar": {
15+
"href": "https://secure.gravatar.com/avatar/ad2424dafaasdssaew12232344434432?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FU4-2.png"
16+
},
17+
},
18+
"nickname": "User04Nickname",
19+
"type": "user",
20+
"account_id": "User04AccountID",
21+
},
22+
"resolved_on": null,
23+
"created_on": "2021-10-19T20:20:49.288763+00:00",
24+
"content": {"raw": "ToDo 1"},
25+
"state": "UNRESOLVED",
26+
"resolved_by": null,
27+
"updated_on": "2021-10-19T20:20:49.288843+00:00",
28+
"id": 123456,
29+
}

tests/test_bitbucket_cloud_oo.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from atlassian import Bitbucket
88
from atlassian.bitbucket import Cloud
99
from atlassian.bitbucket.cloud.common.users import User
10-
from atlassian.bitbucket.cloud.repositories.pullRequests import Comment, Participant, PullRequest, Build
10+
from atlassian.bitbucket.cloud.repositories.pullRequests import Comment, Participant, PullRequest, Build, Task
1111

1212
BITBUCKET = None
1313
try:
@@ -277,6 +277,45 @@ def test_comments(self, tc1):
277277
assert c1.user.display_name == "User04DisplayName"
278278
assert c2.html == "<p>Test comment 2</p>"
279279

280+
def test_add_task(self, tc1):
281+
msg = "ToDo 1"
282+
task = tc1.add_task(msg)
283+
assert isinstance(task, Task)
284+
assert task.id == 123456
285+
assert task.description == msg
286+
assert not task.is_resolved
287+
288+
with pytest.raises(ValueError):
289+
tc1.add_task(None)
290+
291+
def test_update_task(self, tc1):
292+
task = [t for t in tc1.tasks() if t.id == 123456][0]
293+
task = task.update("ToDo 10")
294+
assert task.description == "ToDo 10"
295+
296+
with pytest.raises(ValueError):
297+
task.update(None)
298+
299+
def test_delete_task(self, tc1):
300+
task = [t for t in tc1.tasks() if t.id == 123456][0]
301+
assert task.delete() is None
302+
303+
def test_tasks(self, tc1):
304+
tasks = list(tc1.tasks())
305+
assert len(tasks) == 2
306+
t1, t2 = tasks
307+
assert isinstance(t1, Task)
308+
assert isinstance(t2, Task)
309+
assert t2.id == 123456
310+
assert t1.id == 234567
311+
assert not t2.is_resolved
312+
assert t1.is_resolved
313+
assert isinstance(t2.creator, User)
314+
assert isinstance(t1.resolved_by, User)
315+
assert t2.description == "ToDo 1"
316+
assert _datetimetostr(t1.resolved_on) == _datetimetostr(datetime(2021, 10, 19, 20, 28, 47, 493275))
317+
assert _datetimetostr(t2.created_on) == _datetimetostr(datetime(2021, 10, 19, 20, 20, 49, 288763))
318+
280319
def test_approve(self, tc1):
281320
ap = tc1.approve()
282321
assert ap["approved"]

0 commit comments

Comments
 (0)