Commit 45bb16c2 authored by Meet Mangukiya's avatar Meet Mangukiya

IGitt: Add assign, unassign methods, change assignee property.

- Add assign, unassign methods to Issues.
- Rename assignee property to assignees.
- Remove the assignee property setter.
parent a2217aa7
Pipeline #9002861 passed with stage
in 2 minutes and 6 seconds
......@@ -13,7 +13,7 @@ pylint_disable = R0903, C0411, C0326, C1801, W0231, R0913
[python.code]
files = **/*.py
ignore = tests/**.py
pylint_disable += E1126
pylint_disable += E1126, R1705
[python.tests]
files = tests/**.py
......
......@@ -3,7 +3,7 @@ This contains the Issue implementation for GitHub.
"""
from datetime import datetime
from IGitt.GitHub import get, patch, post
from IGitt.GitHub import get, patch, post, delete
from IGitt.GitHub.GitHubComment import GitHubComment
from IGitt.Interfaces.Comment import CommentType
from IGitt.Interfaces.Issue import Issue
......@@ -93,7 +93,7 @@ class GitHubIssue(Issue):
return self._data['number']
@property
def assignee(self):
def assignees(self):
"""
Retrieves the assignee of the issue:
......@@ -107,17 +107,27 @@ class GitHubIssue(Issue):
... 'gitmate-test-user/test', 2)
>>> issue.assignee # Returns None, unassigned
:return: A string containing the username or None.
:return: A tuple containing the usernames of assignees.
"""
return (self._data['assignee']['login'] if self._data['assignee'] else
None)
return tuple(user['login'] for user in self._data['assignees'])
@assignee.setter
def assignee(self, new_assignee):
self._data = post(self._token,
'/repos/{}/issues/{}/assignees'.format(self._repository,
self.number),
{'assignees': new_assignee})
def assign(self, username: str):
"""
Adds the user as one of the assignees of the issue.
:param username: Username of the user to be added as an assignee.
"""
url = self._url + '/assignees'
self._data = post(self._token, url, {"assignees": [username]})
def unassign(self, username: str):
"""
Removes the user from the assignees of the issue.
:param username: Username of the user to be unassigned.
"""
url = self._url + '/assignees'
delete(self._token, url, {"assignees": [username]})
self._data = get(self._token, self._url)
@property
def description(self):
......
......@@ -67,4 +67,4 @@ def delete(token: str, url: str, params: dict=None):
:raises RuntimeError: If the response indicates any problem.
"""
_fetch(BASE_URL, 'delete', {'access_token': token},
url, query_params=params)
url, params)
......@@ -98,7 +98,7 @@ class GitLabIssue(Issue):
return self._data['iid']
@property
def assignee(self):
def assignees(self):
"""
Retrieves the assignee of the issue:
......@@ -112,21 +112,49 @@ class GitLabIssue(Issue):
... 'gitmate-test-user/test', 2)
>>> issue.assignee # Returns None, unassigned
:return: A string containing the username or None.
"""
# GitLab uses some stupid EE conformance to create a single element
# array of assignee.
return (self._data['assignees'][0]['username']
if self._data['assignees'] else None)
@assignee.setter
def assignee(self, new_assignee):
url = self._url.format(quote_plus(self._repository),
self.number)
res = get(self._token, '/users', {'username': new_assignee})
if len(res) > 0:
user = res[0]['id']
self._data = put(self._token, url, {'assignee_ids': user})
:return: A tuple containing the usernames of assignees.
"""
return tuple(user['username'] for user in self._data['assignees'])
def get_user(self, username: str):
"""
Queries the user resource with given username.
:param username: Username of the user being queried.
:return: json response of the query.
"""
return get(self._token, '/users', {'username': username})[0]
def assign(self, username: str):
"""
Adds the user as one of the assignees of the issue.
:param username: Username of the user to be added as an assignee.
"""
url = '/projects/{repo}/issues/{iid}'.format(
repo=quote_plus(self._repository),
iid=self.number
)
current_assignee_ids = [user['id'] for user in self._data['assignees']]
user = self.get_user(username)
if user['id'] not in current_assignee_ids:
current_assignee_ids.append(user['id'])
self._data = put(self._token, url,
{"assignee_ids": current_assignee_ids})
def unassign(self, username: str):
"""
Removes the user from the assignees of the issue.
:param username: Username of the user to be unassigned.
"""
url = '/projects/{repo}/issues/{iid}'.format(
repo=quote_plus(self._repository),
iid=self.number
)
current_assignee_ids = [user['id'] for user in self._data['assignees']]
user = self.get_user(username)
if user['username'] in self.assignees:
current_assignee_ids.remove(user['id'])
self._data = put(self._token, url,
{'assignee_ids': current_assignee_ids})
@property
def description(self) -> str:
......
......@@ -50,19 +50,21 @@ class Issue:
raise NotImplementedError
@property
def assignee(self) -> str:
def assignees(self) -> tuple:
"""
Retrieves the username of the assigned user or None.
Retrieves a tuple of usernames of assignees.
"""
raise NotImplementedError
@assignee.setter
def assignee(self, username: str):
def assign(self, username: str):
"""
Sets the assignee to the given user.
Sets a given user as assignee.
"""
raise NotImplementedError
:param username: A string containing the username of the user.
:raises RuntimeError: If something goes wrong (network, auth...).
def unassign(self, username: str):
"""
Unassigns given user from issue.
"""
raise NotImplementedError
......
......@@ -32,11 +32,13 @@ class GitHubIssueTest(unittest.TestCase):
@my_vcr.use_cassette('tests/GitHub/cassettes/github_issue_assignee.yaml')
def test_assignee(self):
self.assertEqual(self.iss.assignee, None)
self.assertEqual(self.iss.assignees, tuple())
iss = GitHubIssue(os.environ.get('GITHUB_TEST_TOKEN', ''),
'gitmate-test-user/test', 41)
iss.assignee = 'meetmangukiya'
self.assertEqual(iss.assignee, 'meetmangukiya')
iss.assign('meetmangukiya')
self.assertEqual(iss.assignees, ('meetmangukiya', ))
iss.unassign('meetmangukiya')
self.assertEqual(iss.assignees, tuple())
def test_number(self):
self.assertEqual(self.iss.number, 39)
......
......@@ -9,20 +9,20 @@ interactions:
method: GET
uri: https://gitlab.com/api/v4/projects/gitmate-test-user%2Ftest/issues/27?User-Agent=IGitt
response:
body: {string: '{"id":5685687,"iid":27,"project_id":3439658,"title":"test issue","description":"","state":"opened","created_at":"2017-06-12T18:14:54.571Z","updated_at":"2017-06-12T18:41:11.033Z","labels":[],"milestone":null,"assignees":[],"author":{"name":"Meet
body: {string: '{"id":5685687,"iid":27,"project_id":3439658,"title":"test issue","description":"","state":"opened","created_at":"2017-06-12T18:14:54.571Z","updated_at":"2017-06-13T16:22:24.830Z","labels":[],"milestone":null,"assignees":[],"author":{"name":"Meet
Mangukiya","username":"meetmangukiya","id":707601,"state":"active","avatar_url":"https://gitlab.com/uploads/system/user/avatar/707601/avatar.png","web_url":"https://gitlab.com/meetmangukiya"},"assignee":null,"user_notes_count":0,"upvotes":0,"downvotes":0,"due_date":null,"confidential":false,"weight":null,"web_url":"https://gitlab.com/gitmate-test-user/test/issues/27","subscribed":true}'}
headers:
Cache-Control: ['max-age=0, private, must-revalidate']
Content-Length: ['634']
Content-Type: [application/json]
Date: ['Mon, 12 Jun 2017 19:54:46 GMT']
Etag: [W/"6e93a25fa4396d90b684686707c4aed3"]
Date: ['Tue, 13 Jun 2017 16:23:14 GMT']
Etag: [W/"868345de10bffefeb6d20c2b5f3f25bc"]
Server: [nginx]
Strict-Transport-Security: [max-age=31536000]
Vary: [Origin]
X-Frame-Options: [SAMEORIGIN]
X-Request-Id: [36ce2850-2ac2-466d-b937-b4811abaf4ec]
X-Runtime: ['0.349949']
X-Request-Id: [09d4388f-7c67-4aa0-8f52-874fb1f92c9e]
X-Runtime: ['0.566652']
status: {code: 200, message: OK}
- request:
body: null
......@@ -39,7 +39,7 @@ interactions:
Cache-Control: ['max-age=0, private, must-revalidate']
Content-Length: ['207']
Content-Type: [application/json]
Date: ['Mon, 12 Jun 2017 19:54:53 GMT']
Date: ['Tue, 13 Jun 2017 16:23:21 GMT']
Etag: [W/"dba9fcc1837dc5610687047d786f3314"]
Link: ['<https://gitlab.com/api/v4/users?User-Agent=IGitt&active=false&blocked=false&external=false&page=1&per_page=20&private_token=&skip_ldap=false&username=meetmangukiya>;
rel="first", <https://gitlab.com/api/v4/users?User-Agent=IGitt&active=false&blocked=false&external=false&page=1&per_page=20&private_token=&skip_ldap=false&username=meetmangukiya>;
......@@ -52,24 +52,24 @@ interactions:
X-Page: ['1']
X-Per-Page: ['20']
X-Prev-Page: ['']
X-Request-Id: [6080996e-2593-43f8-bcd2-22d55fb4b67e]
X-Runtime: ['0.103986']
X-Request-Id: [4853b28c-44e7-47a1-8ba7-d8ea5922ecde]
X-Runtime: ['0.126624']
X-Total: ['1']
X-Total-Pages: ['1']
status: {code: 200, message: OK}
- request:
body: '{"assignee_ids": 707601}'
body: '{"assignee_ids": [707601]}'
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
Content-Length: ['24']
Content-Length: ['26']
Content-Type: [application/json]
User-Agent: [python-requests/2.17.3]
method: PUT
uri: https://gitlab.com/api/v4/projects/gitmate-test-user%2Ftest/issues/27?User-Agent=IGitt
response:
body: {string: '{"id":5685687,"iid":27,"project_id":3439658,"title":"test issue","description":"","state":"opened","created_at":"2017-06-12T18:14:54.571Z","updated_at":"2017-06-12T19:55:03.051Z","labels":[],"milestone":null,"assignees":[{"name":"Meet
body: {string: '{"id":5685687,"iid":27,"project_id":3439658,"title":"test issue","description":"","state":"opened","created_at":"2017-06-12T18:14:54.571Z","updated_at":"2017-06-13T16:23:26.574Z","labels":[],"milestone":null,"assignees":[{"name":"Meet
Mangukiya","username":"meetmangukiya","id":707601,"state":"active","avatar_url":"https://gitlab.com/uploads/system/user/avatar/707601/avatar.png","web_url":"https://gitlab.com/meetmangukiya"}],"author":{"name":"Meet
Mangukiya","username":"meetmangukiya","id":707601,"state":"active","avatar_url":"https://gitlab.com/uploads/system/user/avatar/707601/avatar.png","web_url":"https://gitlab.com/meetmangukiya"},"assignee":{"name":"Meet
Mangukiya","username":"meetmangukiya","id":707601,"state":"active","avatar_url":"https://gitlab.com/uploads/system/user/avatar/707601/avatar.png","web_url":"https://gitlab.com/meetmangukiya"},"user_notes_count":0,"upvotes":0,"downvotes":0,"due_date":null,"confidential":false,"weight":null,"web_url":"https://gitlab.com/gitmate-test-user/test/issues/27","subscribed":true}'}
......@@ -77,13 +77,73 @@ interactions:
Cache-Control: ['max-age=0, private, must-revalidate']
Content-Length: ['1040']
Content-Type: [application/json]
Date: ['Mon, 12 Jun 2017 19:55:03 GMT']
Etag: [W/"751afed878653015834545dc96cac866"]
Date: ['Tue, 13 Jun 2017 16:23:27 GMT']
Etag: [W/"e8d0cfa1251cbfe4954e426d5c771c23"]
Server: [nginx]
Strict-Transport-Security: [max-age=31536000]
Vary: [Origin]
X-Frame-Options: [SAMEORIGIN]
X-Request-Id: [4224d9ed-8dbc-4e22-85e7-16d9e8cee6e2]
X-Runtime: ['1.284307']
X-Request-Id: [9d6ebb09-de78-4f59-940d-23f24a73b8fc]
X-Runtime: ['1.411898']
status: {code: 200, message: OK}
- request:
body: null
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
User-Agent: [python-requests/2.17.3]
method: GET
uri: https://gitlab.com/api/v4/users?User-Agent=IGitt&username=meetmangukiya
response:
body: {string: '[{"name":"Meet Mangukiya","username":"meetmangukiya","id":707601,"state":"active","avatar_url":"https://gitlab.com/uploads/system/user/avatar/707601/avatar.png","web_url":"https://gitlab.com/meetmangukiya"}]'}
headers:
Cache-Control: ['max-age=0, private, must-revalidate']
Content-Length: ['207']
Content-Type: [application/json]
Date: ['Tue, 13 Jun 2017 16:23:32 GMT']
Etag: [W/"dba9fcc1837dc5610687047d786f3314"]
Link: ['<https://gitlab.com/api/v4/users?User-Agent=IGitt&active=false&blocked=false&external=false&page=1&per_page=20&private_token=&skip_ldap=false&username=meetmangukiya>;
rel="first", <https://gitlab.com/api/v4/users?User-Agent=IGitt&active=false&blocked=false&external=false&page=1&per_page=20&private_token=&skip_ldap=false&username=meetmangukiya>;
rel="last"']
Server: [nginx]
Strict-Transport-Security: [max-age=31536000]
Vary: [Origin]
X-Frame-Options: [SAMEORIGIN]
X-Next-Page: ['']
X-Page: ['1']
X-Per-Page: ['20']
X-Prev-Page: ['']
X-Request-Id: [bb187514-08e0-45ef-9c3d-e5d831d02f19]
X-Runtime: ['0.132508']
X-Total: ['1']
X-Total-Pages: ['1']
status: {code: 200, message: OK}
- request:
body: '{"assignee_ids": []}'
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
Content-Length: ['20']
Content-Type: [application/json]
User-Agent: [python-requests/2.17.3]
method: PUT
uri: https://gitlab.com/api/v4/projects/gitmate-test-user%2Ftest/issues/27?User-Agent=IGitt
response:
body: {string: '{"id":5685687,"iid":27,"project_id":3439658,"title":"test issue","description":"","state":"opened","created_at":"2017-06-12T18:14:54.571Z","updated_at":"2017-06-13T16:23:38.705Z","labels":[],"milestone":null,"assignees":[],"author":{"name":"Meet
Mangukiya","username":"meetmangukiya","id":707601,"state":"active","avatar_url":"https://gitlab.com/uploads/system/user/avatar/707601/avatar.png","web_url":"https://gitlab.com/meetmangukiya"},"assignee":null,"user_notes_count":0,"upvotes":0,"downvotes":0,"due_date":null,"confidential":false,"weight":null,"web_url":"https://gitlab.com/gitmate-test-user/test/issues/27","subscribed":true}'}
headers:
Cache-Control: ['max-age=0, private, must-revalidate']
Content-Length: ['634']
Content-Type: [application/json]
Date: ['Tue, 13 Jun 2017 16:23:38 GMT']
Etag: [W/"858a376b01b717c5aea977fd0fccd886"]
Server: [nginx]
Strict-Transport-Security: [max-age=31536000]
Vary: [Origin]
X-Frame-Options: [SAMEORIGIN]
X-Request-Id: [49541e97-ed0e-4f72-82c5-d6bab52caaa4]
X-Runtime: ['0.640220']
status: {code: 200, message: OK}
version: 1
......@@ -32,12 +32,13 @@ class GitLabIssueTest(unittest.TestCase):
@my_vcr.use_cassette('tests/GitLab/cassettes/gitlab_issue_assignee.yaml')
def test_assignee(self):
self.assertIsNone(self.iss.assignee)
self.assertEqual(self.iss.assignees, tuple())
iss = GitLabIssue(os.environ.get('GITLAB_TEST_TOKEN', ''),
'gitmate-test-user/test', 27)
self.assertEqual(iss.assignee, None)
iss.assignee = 'meetmangukiya'
self.assertEqual(iss.assignee, 'meetmangukiya')
iss.assign('meetmangukiya')
self.assertEqual(iss.assignees, ('meetmangukiya', ))
iss.unassign('meetmangukiya')
self.assertEqual(iss.assignees, tuple())
def test_number(self):
self.assertEqual(self.iss.number, 3)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment