Commit e2224d7d authored by Vamshi Krishna's avatar Vamshi Krishna

Add Team class

Closes #94, coala/GSoC/GSoC-2018#286
parent 726fde07
Pipeline #25302407 passed with stages
in 30 seconds
"""
Contains the GitHub Team implementation.
"""
from typing import Set
from IGitt.GitHub import GH_INSTANCE_URL
from IGitt.GitHub import GitHubMixin
from IGitt.GitHub.GitHubUser import GitHubUser
from IGitt.GitHub.GitHubOrganization import GitHubOrganization
from IGitt.Interfaces.Team import Team
from IGitt.Interfaces import get
class GitHubTeam(GitHubMixin, Team):
"""
Represents a team on GitHub.
"""
def __init__(self, token, team_id):
"""
:param team_id: The id of the team.
"""
self._token = token
self._id = team_id
self._url = '/teams/{team_id}'.format(team_id=team_id)
@property
def name(self) -> str:
"""
Name of the team.
"""
return self.data['name']
@property
def id(self) -> int:
"""
Retrieves the id of the team.
"""
return self._id
@property
def description(self) -> str:
"""
Returns the description of this team.
"""
return self.data['description']
@property
def web_url(self):
return '{}/orgs/{}/teams/{}'.format(GH_INSTANCE_URL,
self.get_organization.name,
self.name)
@property
def members(self) -> Set[GitHubUser]:
"""
Returns the user handles of all members of this team.
"""
return {
GitHubUser.from_data(user, self._token, user['login'])
for user in get(
self._token, self.url + '/members'
)
}
def is_member(self, username):
"""
Checks if given username is member of this team.
"""
usernames = [user.username for user in self.members]
return True if username in usernames else False
@property
def get_organization(self):
"""
Returns parent organization.
"""
return GitHubOrganization.from_data(self.data['organization'],
self._token,
self.data['organization']['login'])
"""
Contains the GitLab Team implementation.
"""
from typing import Set
from IGitt.GitLab import GitLabMixin
from IGitt.GitLab.GitLabUser import GitLabUser
from IGitt.Interfaces import get
class GitLabTeam(GitLabMixin):
"""
Represents a Team on GitLab.
"""
def __init__(self, token, group_id):
"""
:param group_id: The id of the group.
"""
self._token = token
self._id = group_id
self._url = '/groups/{group_id}'.format(group_id=group_id)
@property
def name(self) -> str:
"""
Name of the team.
"""
return self.data['name']
@property
def id(self) -> int:
"""
Retrieves the id of the team.
"""
return self._id
@property
def description(self) -> str:
"""
Returns the description of this team.
"""
return self.data['description']
@property
def members(self) -> Set[GitLabUser]:
"""
Returns the user handles of all members of this team.
"""
return {
GitLabUser.from_data(user, self._token, user['id'])
for user in get(
self._token, self.url + '/members'
)
}
def is_member(self, username):
"""
Checks if given username is member of this team.
"""
usernames = [user.username for user in self.members]
return True if username in usernames else False
@property
def get_organization(self):
"""
Returns parent team.
"""
if self.data['parent_id']:
return GitLabTeam(self._token, self.data['parent_id'])
return None
"""
Contains the Team abstraction class.
"""
from typing import Set
from IGitt.Interfaces import IGittObject
from IGitt.Interfaces.User import User
class Team(IGittObject):
"""
Represents a team on GitHub or GitLab.
"""
@property
def name(self) -> str:
"""
Name of the team.
"""
raise NotImplementedError
@property
def id(self) -> int:
"""
Retrieves the id of the team.
"""
raise NotImplementedError
@property
def description(self) -> str:
"""
Returns the description of this team.
"""
raise NotImplementedError
@property
def members(self) -> Set[User]:
"""
Returns the user handles of all members of this team.
"""
raise NotImplementedError
def is_member(self, username: str):
"""
Checks if given username is member of this team.
"""
raise NotImplementedError
@property
def get_organization(self):
"""
Returns parent organization.
"""
raise NotImplementedError
interactions:
- request:
body: '{}'
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
Content-Length: ['2']
Content-Type: [application/json]
User-Agent: [IGitt]
method: GET
uri: https://api.github.com/teams/2809871?per_page=100
response:
body:
string: !!binary |
H4sIAAAAAAAAA6VUXW+bMBT9K8jPoRCHfIBUTZPaVX3IoklRNfUFGXDBicGWbZKlUf77roFsNENL
173hyz3Hvveee46oIiVFETKUlGiEWIYivPDDxXw8QpXIaGwjaHn3bfb08FQk68dgebcNvr7e30K2
5nX+G5tRnSomDRMVBDNBtVMJU7Aqd+iPlErjvAjlECcRxjHCMVQbZ89MAURSsR1JDwBLudA0g1Ct
OBwLY6SOPI9IdpNDbp3cpKL07Gu11z0UkktaJlTp+P0gr4Mcu48TsCgqhWZGKEb/iarB2SqoKpnW
bf2y5hxiqaLE0CwmBqrB/njh+jMXh2s/jPA0wrNnW6rMruacK0xFXQHVpHtt3J39ERIqJxV7JW3/
j4iLnNlBQNtKoHdtv11IggvtTCcY+0GIAdgf8/1htQ0Pz/hLTb7LInvgu2SzPCw3n/3V+nF/bSzA
rr2B+5oG/X04g1Dv3Fm6o5X5EEOLhIcXQmw/xNAAbdO0rq/pYriKFgkU75LpMMeAXGWdcJbG/0H6
lqG/CmRHDFGXI2uCGnebWGuqUlEZGE2zlLV31tSn3W0A5b41BAh0XjOgEMBLUoEBVLA2I5SAeEG6
AOEi7RTd/qElYdYY7ESJjvuij6USG5oajSKjatom/Nrpwx+/u+pbjUUY9rdtaM605YDNeBGciz0Y
S+8EdtacClPyi/70DGqoxAsnmLt+6GK8Hs+j6SSaBkNOMHfH2PUXaz+IgkXkY5tjDtIa9qq/7qfT
T/McN93JBQAA
headers:
Access-Control-Allow-Origin: ['*']
Access-Control-Expose-Headers: ['ETag, Link, Retry-After, X-GitHub-OTP, X-RateLimit-Limit,
X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes,
X-Poll-Interval']
Cache-Control: ['private, max-age=60, s-maxage=60']
Content-Encoding: [gzip]
Content-Security-Policy: [default-src 'none']
Content-Type: [application/json; charset=utf-8]
Date: ['Fri, 29 Jun 2018 11:47:30 GMT']
ETag: [W/"7323e573b6a7ba3ca5482cf44851b5b0"]
Last-Modified: ['Fri, 29 Jun 2018 09:25:26 GMT']
Referrer-Policy: ['origin-when-cross-origin, strict-origin-when-cross-origin']
Server: [GitHub.com]
Status: [200 OK]
Strict-Transport-Security: [max-age=31536000; includeSubdomains; preload]
Vary: ['Accept, Authorization, Cookie, X-GitHub-OTP']
X-Accepted-OAuth-Scopes: ['admin:org, read:org, repo, user, write:org']
X-Content-Type-Options: [nosniff]
X-Frame-Options: [deny]
X-GitHub-Media-Type: [github.v3; format=json]
X-GitHub-Request-Id: ['2FC7:6972:6FFE82:FC5CCC:5B361C51']
X-OAuth-Scopes: ['admin:gpg_key, admin:org, admin:org_hook, admin:public_key,
admin:repo_hook, gist, notifications, repo, user']
X-RateLimit-Limit: ['5000']
X-RateLimit-Remaining: ['4995']
X-RateLimit-Reset: ['1530275927']
X-Runtime-rack: ['0.046046']
X-XSS-Protection: [1; mode=block]
status: {code: 200, message: OK}
version: 1
interactions:
- request:
body: '{}'
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
Content-Length: ['2']
Content-Type: [application/json]
If-None-Match: [W/"7323e573b6a7ba3ca5482cf44851b5b0"]
User-Agent: [IGitt]
method: GET
uri: https://api.github.com/teams/2809871?per_page=100
response:
body: {string: ''}
headers:
Access-Control-Allow-Origin: ['*']
Access-Control-Expose-Headers: ['ETag, Link, Retry-After, X-GitHub-OTP, X-RateLimit-Limit,
X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes,
X-Poll-Interval']
Cache-Control: ['private, max-age=60, s-maxage=60']
Content-Security-Policy: [default-src 'none']
Content-Type: [application/octet-stream]
Date: ['Fri, 29 Jun 2018 11:47:32 GMT']
ETag: ['"7323e573b6a7ba3ca5482cf44851b5b0"']
Last-Modified: ['Fri, 29 Jun 2018 09:25:26 GMT']
Referrer-Policy: ['origin-when-cross-origin, strict-origin-when-cross-origin']
Server: [GitHub.com]
Status: [304 Not Modified]
Strict-Transport-Security: [max-age=31536000; includeSubdomains; preload]
Vary: ['Accept, Authorization, Cookie, X-GitHub-OTP']
X-Content-Type-Options: [nosniff]
X-Frame-Options: [deny]
X-GitHub-Request-Id: ['2FC8:6974:10CFF79:20A3B75:5B361C53']
X-RateLimit-Limit: ['5000']
X-RateLimit-Remaining: ['4995']
X-RateLimit-Reset: ['1530275927']
X-Runtime-rack: ['0.054053']
X-XSS-Protection: [1; mode=block]
status: {code: 304, message: Not Modified}
version: 1
interactions:
- request:
body: '{}'
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
Content-Length: ['2']
Content-Type: [application/json]
User-Agent: [IGitt]
method: GET
uri: https://api.github.com/teams/2809871/members?per_page=100
response:
body:
string: !!binary |
H4sIAAAAAAAAA62VX0+rMByGvwvX0yI74lhycmKyc3EuNnMSNRpzslRWoRNa0hbmJH53f/wZK5y5
CN3d0vR9+rblWZ9yK+IBZdbUCqiKsSJnikh1lkoirJFFV9b0wnUnF/bYHlmMr8iyGLLms7/u/cMi
8te/ncU6eJvP5huYjjOssFimIoI5oVKJnCJUDcrxOSwQps8F2edMEabOfR6jFO0W+JX9/AGQQNSY
ciUY6OASWpOqOOAkOtQ9VHHUqVI1KHOHEi88ivgGeN0dfGtJ1MShc/WbssAEBfEccRUSOFLY5kdx
OFSqgfXKaA4blwousYBJuCxBVsMq1mEouGHQLUeCJLykps/SFzRRlLOBVVsIQHIRYEbfsQESEBJI
RcmBpcooIEgGn+5ARpXNUSJohv1tcVyC+IRmcAsm3A4EsGqbENDnrvJYUkWWeBUXor/gSJKP0V58
9gp1mE9s+wqCpfJXju1MvK+UH8/X19ub2auB8vUCBsq3Wx+XvT23l+ZadLjg/0NM1NZpRlLroNPp
3KLq/wVDRNZhfRXWs/3l1dOn0bbdp2V9P2HvcSxD6nm1rY7tud7lxD38QP/Z3Nw+Xi5uAwemH3ug
nS8f6N0CBrZqlY+rqk3s5ekuN1zSDsHE0AZlpGdDOZ2be6SpmA2pr5VNsL+STfQ0PmpN+sr47xO1
668wOAsAAA==
headers:
Access-Control-Allow-Origin: ['*']
Access-Control-Expose-Headers: ['ETag, Link, Retry-After, X-GitHub-OTP, X-RateLimit-Limit,
X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes,
X-Poll-Interval']
Cache-Control: ['private, max-age=60, s-maxage=60']
Content-Encoding: [gzip]
Content-Security-Policy: [default-src 'none']
Content-Type: [application/json; charset=utf-8]
Date: ['Fri, 29 Jun 2018 11:47:34 GMT']
ETag: [W/"e85db7b970d6e816dc31066e58c39f7a"]
Referrer-Policy: ['origin-when-cross-origin, strict-origin-when-cross-origin']
Server: [GitHub.com]
Status: [200 OK]
Strict-Transport-Security: [max-age=31536000; includeSubdomains; preload]
Vary: ['Accept, Authorization, Cookie, X-GitHub-OTP']
X-Accepted-OAuth-Scopes: ['admin:org, read:org, repo, user, write:org']
X-Content-Type-Options: [nosniff]
X-Frame-Options: [deny]
X-GitHub-Media-Type: [github.v3; format=json]
X-GitHub-Request-Id: ['2FC9:6975:D36FE6:1BE5C4A:5B361C55']
X-OAuth-Scopes: ['admin:gpg_key, admin:org, admin:org_hook, admin:public_key,
admin:repo_hook, gist, notifications, repo, user']
X-RateLimit-Limit: ['5000']
X-RateLimit-Remaining: ['4994']
X-RateLimit-Reset: ['1530275927']
X-Runtime-rack: ['0.068670']
X-XSS-Protection: [1; mode=block]
status: {code: 200, message: OK}
- request:
body: '{}'
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
Content-Length: ['2']
Content-Type: [application/json]
If-None-Match: [W/"e85db7b970d6e816dc31066e58c39f7a"]
User-Agent: [IGitt]
method: GET
uri: https://api.github.com/teams/2809871/members?per_page=100
response:
body: {string: ''}
headers:
Access-Control-Allow-Origin: ['*']
Access-Control-Expose-Headers: ['ETag, Link, Retry-After, X-GitHub-OTP, X-RateLimit-Limit,
X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes,
X-Poll-Interval']
Cache-Control: ['private, max-age=60, s-maxage=60']
Content-Security-Policy: [default-src 'none']
Content-Type: [application/octet-stream]
Date: ['Sat, 30 Jun 2018 12:30:03 GMT']
ETag: ['"e85db7b970d6e816dc31066e58c39f7a"']
Referrer-Policy: ['origin-when-cross-origin, strict-origin-when-cross-origin']
Server: [GitHub.com]
Status: [304 Not Modified]
Strict-Transport-Security: [max-age=31536000; includeSubdomains; preload]
Vary: ['Accept, Authorization, Cookie, X-GitHub-OTP']
X-Content-Type-Options: [nosniff]
X-Frame-Options: [deny]
X-GitHub-Request-Id: ['0BAF:6974:244E908:464DBBB:5B3777CA']
X-RateLimit-Limit: ['5000']
X-RateLimit-Remaining: ['5000']
X-RateLimit-Reset: ['1530365403']
X-Runtime-rack: ['0.056860']
X-XSS-Protection: [1; mode=block]
status: {code: 304, message: Not Modified}
version: 1
interactions:
- request:
body: '{}'
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
Content-Length: ['2']
Content-Type: [application/json]
If-None-Match: [W/"e85db7b970d6e816dc31066e58c39f7a"]
User-Agent: [IGitt]
method: GET
uri: https://api.github.com/teams/2809871/members?per_page=100
response:
body: {string: ''}
headers:
Access-Control-Allow-Origin: ['*']
Access-Control-Expose-Headers: ['ETag, Link, Retry-After, X-GitHub-OTP, X-RateLimit-Limit,
X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes,
X-Poll-Interval']
Cache-Control: ['private, max-age=60, s-maxage=60']
Content-Security-Policy: [default-src 'none']
Content-Type: [application/octet-stream]
Date: ['Fri, 29 Jun 2018 11:47:36 GMT']
ETag: ['"e85db7b970d6e816dc31066e58c39f7a"']
Referrer-Policy: ['origin-when-cross-origin, strict-origin-when-cross-origin']
Server: [GitHub.com]
Status: [304 Not Modified]
Strict-Transport-Security: [max-age=31536000; includeSubdomains; preload]
Vary: ['Accept, Authorization, Cookie, X-GitHub-OTP']
X-Content-Type-Options: [nosniff]
X-Frame-Options: [deny]
X-GitHub-Request-Id: ['2FCA:6973:908D48:1478A4C:5B361C57']
X-RateLimit-Limit: ['5000']
X-RateLimit-Remaining: ['4994']
X-RateLimit-Reset: ['1530275927']
X-Runtime-rack: ['0.052751']
X-XSS-Protection: [1; mode=block]
status: {code: 304, message: Not Modified}
version: 1
interactions:
- request:
body: '{}'
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
Content-Length: ['2']
Content-Type: [application/json]
If-None-Match: [W/"7323e573b6a7ba3ca5482cf44851b5b0"]
User-Agent: [IGitt]
method: GET
uri: https://api.github.com/teams/2809871?per_page=100
response:
body: {string: ''}
headers:
Access-Control-Allow-Origin: ['*']
Access-Control-Expose-Headers: ['ETag, Link, Retry-After, X-GitHub-OTP, X-RateLimit-Limit,
X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes,
X-Poll-Interval']
Cache-Control: ['private, max-age=60, s-maxage=60']
Content-Security-Policy: [default-src 'none']
Content-Type: [application/octet-stream]
Date: ['Fri, 29 Jun 2018 11:47:37 GMT']
ETag: ['"7323e573b6a7ba3ca5482cf44851b5b0"']
Last-Modified: ['Fri, 29 Jun 2018 09:25:26 GMT']
Referrer-Policy: ['origin-when-cross-origin, strict-origin-when-cross-origin']
Server: [GitHub.com]
Status: [304 Not Modified]
Strict-Transport-Security: [max-age=31536000; includeSubdomains; preload]
Vary: ['Accept, Authorization, Cookie, X-GitHub-OTP']
X-Content-Type-Options: [nosniff]
X-Frame-Options: [deny]
X-GitHub-Request-Id: ['2FCB:6972:7000B0:FC620B:5B361C58']
X-RateLimit-Limit: ['5000']
X-RateLimit-Remaining: ['4994']
X-RateLimit-Reset: ['1530275927']
X-Runtime-rack: ['0.042269']
X-XSS-Protection: [1; mode=block]
status: {code: 304, message: Not Modified}
version: 1
interactions:
- request:
body: '{}'
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
Content-Length: ['2']
Content-Type: [application/json]
If-None-Match: [W/"7323e573b6a7ba3ca5482cf44851b5b0"]
User-Agent: [IGitt]
method: GET
uri: https://api.github.com/teams/2809871?per_page=100
response:
body: {string: ''}
headers:
Access-Control-Allow-Origin: ['*']
Access-Control-Expose-Headers: ['ETag, Link, Retry-After, X-GitHub-OTP, X-RateLimit-Limit,
X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes,
X-Poll-Interval']
Cache-Control: ['private, max-age=60, s-maxage=60']
Content-Security-Policy: [default-src 'none']
Content-Type: [application/octet-stream]
Date: ['Fri, 29 Jun 2018 12:17:37 GMT']
ETag: ['"7323e573b6a7ba3ca5482cf44851b5b0"']
Last-Modified: ['Fri, 29 Jun 2018 09:25:26 GMT']
Referrer-Policy: ['origin-when-cross-origin, strict-origin-when-cross-origin']
Server: [GitHub.com]
Status: [304 Not Modified]
Strict-Transport-Security: [max-age=31536000; includeSubdomains; preload]
Vary: ['Accept, Authorization, Cookie, X-GitHub-OTP']
X-Content-Type-Options: [nosniff]
X-Frame-Options: [deny]
X-GitHub-Request-Id: ['3200:6974:113D92E:217C26E:5B362360']
X-RateLimit-Limit: ['5000']
X-RateLimit-Remaining: ['4994']
X-RateLimit-Reset: ['1530275927']
X-Runtime-rack: ['0.052827']
X-XSS-Protection: [1; mode=block]
status: {code: 304, message: Not Modified}
version: 1
import os
from IGitt.GitHub import GitHubToken
from IGitt.GitHub.GitHubTeam import GitHubTeam
from tests import IGittTestCase
class GitHubTeamTest(IGittTestCase):
def setUp(self):
self.token = GitHubToken(os.environ.get('GITHUB_TEST_TOKEN', ''))
self.team = GitHubTeam(self.token, 2809871)
def test_name(self):
self.assertEqual(self.team.name, 'team')
def test_id(self):
self.assertEqual(self.team.id, 2809871)
def test_web_url(self):
self.assertEqual(self.team.web_url,
'https://github.com/orgs/gitmate-test-org/teams/team')
def test_description(self):
self.assertEqual(self.team.description,
'does nothing except for a bot to test with')
def test_members(self):
self.assertEqual({user.username for user in self.team.members},
{'nkprince007', 'Vamshi99', 'gitmate-test-user'})
def test_is_member(self):
self.assertEqual(self.team.is_member('Vamshi99'), True)
self.assertEqual(self.team.is_member('sils'), False)
def test_get_organization(self):
self.assertEqual(self.team.get_organization.name, 'gitmate-test-org')
interactions:
- request:
body: '{}'
headers:
Accept: ['*/*']
Accept-Encoding: ['gzip, deflate']
Connection: [keep-alive]
Content-Length: ['2']
Content-Type: [application/json]
User-Agent: [IGitt]
method: GET
uri: https://gitlab.com/api/v4/groups/2614704?per_page=100
response:
body: {string: '{"id":2614704,"web_url":"https://gitlab.com/groups/gitmate-test-org/another-subgroup/nested-subgroup","name":"nested-subgroup","path":"nested-subgroup","description":"This