Team.py
1 ############################ Copyrights and license ############################ 2 # # 3 # Copyright 2012 Vincent Jacques <vincent@vincent-jacques.net> # 4 # Copyright 2012 Zearin <zearin@gonk.net> # 5 # Copyright 2013 AKFish <akfish@gmail.com> # 6 # Copyright 2013 Vincent Jacques <vincent@vincent-jacques.net> # 7 # Copyright 2013 martinqt <m.ki2@laposte.net> # 8 # Copyright 2014 Jan Orel <jan.orel@gooddata.com> # 9 # Copyright 2014 Vincent Jacques <vincent@vincent-jacques.net> # 10 # Copyright 2015 Aron Culotta <aronwc@gmail.com> # 11 # Copyright 2016 Jannis Gebauer <ja.geb@me.com> # 12 # Copyright 2016 Peter Buckley <dx-pbuckley@users.noreply.github.com> # 13 # Copyright 2016 mattjmorrison <mattjmorrison@mattjmorrison.com> # 14 # Copyright 2018 Isuru Fernando <isuruf@gmail.com> # 15 # Copyright 2018 Jacopo Notarstefano <jacopo.notarstefano@gmail.com> # 16 # Copyright 2018 James D'Amato <james.j.damato@gmail.com> # 17 # Copyright 2018 Maarten Fonville <mfonville@users.noreply.github.com> # 18 # Copyright 2018 Manu Hortet <manuhortet@gmail.com> # 19 # Copyright 2018 Michał Górny <mgorny@gentoo.org> # 20 # Copyright 2018 Steve Kowalik <steven@wedontsleep.org> # 21 # Copyright 2018 Tim Boring <tboring@hearst.com> # 22 # Copyright 2018 sfdye <tsfdye@gmail.com> # 23 # # 24 # This file is part of PyGithub. # 25 # http://pygithub.readthedocs.io/ # 26 # # 27 # PyGithub is free software: you can redistribute it and/or modify it under # 28 # the terms of the GNU Lesser General Public License as published by the Free # 29 # Software Foundation, either version 3 of the License, or (at your option) # 30 # any later version. # 31 # # 32 # PyGithub is distributed in the hope that it will be useful, but WITHOUT ANY # 33 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # 34 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # 35 # details. # 36 # # 37 # You should have received a copy of the GNU Lesser General Public License # 38 # along with PyGithub. If not, see <http://www.gnu.org/licenses/>. # 39 # # 40 ################################################################################ 41 from __future__ import annotations 42 43 from typing import TYPE_CHECKING, Any 44 45 from deprecated import deprecated 46 47 import github.NamedUser 48 import github.Organization 49 import github.PaginatedList 50 import github.Repository 51 import github.TeamDiscussion 52 from github import Consts 53 from github.GithubException import UnknownObjectException 54 from github.GithubObject import Attribute, CompletableGithubObject, NotSet, Opt 55 56 if TYPE_CHECKING: 57 from github.Membership import Membership 58 from github.NamedUser import NamedUser 59 from github.Organization import Organization 60 from github.PaginatedList import PaginatedList 61 from github.Permissions import Permissions 62 from github.Repository import Repository 63 from github.TeamDiscussion import TeamDiscussion 64 65 66 class Team(CompletableGithubObject): 67 """ 68 This class represents Teams. The reference can be found here https://docs.github.com/en/rest/reference/teams 69 """ 70 71 def _initAttributes(self) -> None: 72 self._id: Attribute[int] = NotSet 73 self._members_count: Attribute[int] = NotSet 74 self._members_url: Attribute[str] = NotSet 75 self._name: Attribute[str] = NotSet 76 self._description: Attribute[str] = NotSet 77 self._permission: Attribute[str] = NotSet 78 self._repos_count: Attribute[int] = NotSet 79 self._repositories_url: Attribute[str] = NotSet 80 self._slug: Attribute[str] = NotSet 81 self._url: Attribute[str] = NotSet 82 self._organization: Attribute[github.Organization.Organization] = NotSet 83 self._privacy: Attribute[str] = NotSet 84 self._parent: Attribute[github.Team.Team] = NotSet 85 self._html_url: Attribute[str] = NotSet 86 87 def __repr__(self) -> str: 88 return self.get__repr__({"id": self._id.value, "name": self._name.value}) 89 90 @property 91 def id(self) -> int: 92 self._completeIfNotSet(self._id) 93 return self._id.value 94 95 @property 96 def members_count(self) -> int: 97 self._completeIfNotSet(self._members_count) 98 return self._members_count.value 99 100 @property 101 def members_url(self) -> str: 102 self._completeIfNotSet(self._members_url) 103 return self._members_url.value 104 105 @property 106 def name(self) -> str: 107 self._completeIfNotSet(self._name) 108 return self._name.value 109 110 @property 111 def description(self) -> str: 112 self._completeIfNotSet(self._description) 113 return self._description.value 114 115 @property 116 def permission(self) -> str: 117 self._completeIfNotSet(self._permission) 118 return self._permission.value 119 120 @property 121 def repos_count(self) -> int: 122 self._completeIfNotSet(self._repos_count) 123 return self._repos_count.value 124 125 @property 126 def repositories_url(self) -> str: 127 self._completeIfNotSet(self._repositories_url) 128 return self._repositories_url.value 129 130 @property 131 def slug(self) -> str: 132 self._completeIfNotSet(self._slug) 133 return self._slug.value 134 135 @property 136 def url(self) -> str: 137 self._completeIfNotSet(self._url) 138 return self._url.value 139 140 @property 141 def organization(self) -> Organization: 142 self._completeIfNotSet(self._organization) 143 return self._organization.value 144 145 @property 146 def privacy(self) -> str: 147 self._completeIfNotSet(self._privacy) 148 return self._privacy.value 149 150 @property 151 def parent(self) -> Team: 152 self._completeIfNotSet(self._parent) 153 return self._parent.value 154 155 @property 156 def html_url(self) -> str: 157 self._completeIfNotSet(self._html_url) 158 return self._html_url.value 159 160 def add_to_members(self, member: NamedUser) -> None: 161 """ 162 This API call is deprecated. Use `add_membership` instead. 163 https://docs.github.com/en/rest/reference/teams#add-or-update-team-membership-for-a-user-legacy 164 165 :calls: `PUT /teams/{id}/members/{user} <https://docs.github.com/en/rest/reference/teams>`_ 166 """ 167 assert isinstance(member, github.NamedUser.NamedUser), member 168 headers, data = self._requester.requestJsonAndCheck("PUT", f"{self.url}/members/{member._identity}") 169 170 def add_membership(self, member: NamedUser, role: Opt[str] = NotSet) -> None: 171 """ 172 :calls: `PUT /teams/{id}/memberships/{user} <https://docs.github.com/en/rest/reference/teams>`_ 173 """ 174 assert isinstance(member, github.NamedUser.NamedUser), member 175 assert role is NotSet or isinstance(role, str), role 176 if role is not NotSet: 177 assert role in ["member", "maintainer"] 178 put_parameters = { 179 "role": role, 180 } 181 else: 182 put_parameters = { 183 "role": "member", 184 } 185 headers, data = self._requester.requestJsonAndCheck( 186 "PUT", f"{self.url}/memberships/{member._identity}", input=put_parameters 187 ) 188 189 def get_team_membership(self, member: str | NamedUser) -> Membership: 190 """ 191 :calls: `GET /orgs/{org}/memberships/team/{team_id}/{username} <https://docs.github.com/en/rest/reference/teams#get-team-membership-for-a-user>`_ 192 """ 193 assert isinstance(member, str) or isinstance(member, github.NamedUser.NamedUser), member 194 if isinstance(member, github.NamedUser.NamedUser): 195 member = member._identity 196 headers, data = self._requester.requestJsonAndCheck("GET", f"{self.url}/memberships/{member}") 197 return github.Membership.Membership(self._requester, headers, data, completed=True) 198 199 def add_to_repos(self, repo: Repository) -> None: 200 """ 201 :calls: `PUT /teams/{id}/repos/{org}/{repo} <https://docs.github.com/en/rest/reference/teams>`_ 202 """ 203 assert isinstance(repo, github.Repository.Repository), repo 204 headers, data = self._requester.requestJsonAndCheck("PUT", f"{self.url}/repos/{repo._identity}") 205 206 def get_repo_permission(self, repo: Repository) -> Permissions | None: 207 """ 208 :calls: `GET /teams/{id}/repos/{org}/{repo} <https://docs.github.com/en/rest/reference/teams>`_ 209 """ 210 assert isinstance(repo, github.Repository.Repository) or isinstance(repo, str), repo 211 if isinstance(repo, github.Repository.Repository): 212 repo = repo._identity # type: ignore 213 try: 214 headers, data = self._requester.requestJsonAndCheck( 215 "GET", 216 f"{self.url}/repos/{repo}", 217 headers={"Accept": Consts.teamRepositoryPermissions}, 218 ) 219 return github.Permissions.Permissions(self._requester, headers, data["permissions"], completed=True) 220 except UnknownObjectException: 221 return None 222 223 @deprecated( 224 reason=""" 225 Team.set_repo_permission() is deprecated, use Team.update_team_repository() instead. 226 """ 227 ) 228 def set_repo_permission(self, repo: Repository, permission: str) -> None: 229 """ 230 :calls: `PUT /teams/{id}/repos/{org}/{repo} <https://docs.github.com/en/rest/reference/teams>`_ 231 :param repo: :class:`github.Repository.Repository` 232 :param permission: string 233 :rtype: None 234 """ 235 236 assert isinstance(repo, github.Repository.Repository), repo 237 put_parameters = { 238 "permission": permission, 239 } 240 headers, data = self._requester.requestJsonAndCheck( 241 "PUT", f"{self.url}/repos/{repo._identity}", input=put_parameters 242 ) 243 244 def update_team_repository(self, repo: Repository, permission: str) -> bool: 245 """ 246 :calls: `PUT /orgs/{org}/teams/{team_slug}/repos/{owner}/{repo} <https://docs.github.com/en/rest/reference/teams#check-team-permissions-for-a-repository>`_ 247 """ 248 assert isinstance(repo, github.Repository.Repository) or isinstance(repo, str), repo 249 assert isinstance(permission, str), permission 250 if isinstance(repo, github.Repository.Repository): 251 repo_url_param = repo._identity 252 else: 253 repo_url_param = repo 254 put_parameters = { 255 "permission": permission, 256 } 257 status, _, _ = self._requester.requestJson( 258 "PUT", 259 f"{self.organization.url}/teams/{self.slug}/repos/{repo_url_param}", 260 input=put_parameters, 261 ) 262 return status == 204 263 264 def delete(self) -> None: 265 """ 266 :calls: `DELETE /teams/{id} <https://docs.github.com/en/rest/reference/teams#delete-a-team>`_ 267 """ 268 headers, data = self._requester.requestJsonAndCheck("DELETE", self.url) 269 270 def edit( 271 self, 272 name: str, 273 description: Opt[str] = NotSet, 274 permission: Opt[str] = NotSet, 275 privacy: Opt[str] = NotSet, 276 ) -> None: 277 """ 278 :calls: `PATCH /teams/{id} <https://docs.github.com/en/rest/reference/teams#update-a-team>`_ 279 """ 280 assert isinstance(name, str), name 281 assert description is NotSet or isinstance(description, str), description 282 assert permission is NotSet or isinstance(permission, str), permission 283 assert privacy is NotSet or isinstance(privacy, str), privacy 284 post_parameters = NotSet.remove_unset_items( 285 {"name": name, "description": description, "permission": permission, "privacy": privacy} 286 ) 287 288 headers, data = self._requester.requestJsonAndCheck("PATCH", self.url, input=post_parameters) 289 self._useAttributes(data) 290 291 def get_teams(self) -> PaginatedList[Team]: 292 """ 293 :calls: `GET /teams/{id}/teams <https://docs.github.com/en/rest/reference/teams#list-teams>`_ 294 """ 295 return github.PaginatedList.PaginatedList( 296 github.Team.Team, 297 self._requester, 298 f"{self.url}/teams", 299 None, 300 ) 301 302 def get_discussions(self) -> PaginatedList[TeamDiscussion]: 303 """ 304 :calls: `GET /teams/{id}/discussions <https://docs.github.com/en/rest/reference/teams#list-discussions>`_ 305 """ 306 return github.PaginatedList.PaginatedList( 307 github.TeamDiscussion.TeamDiscussion, 308 self._requester, 309 f"{self.url}/discussions", 310 None, 311 headers={"Accept": Consts.mediaTypeTeamDiscussionsPreview}, 312 ) 313 314 def get_members(self, role: Opt[str] = NotSet) -> PaginatedList[NamedUser]: 315 """ 316 :calls: `GET /teams/{id}/members <https://docs.github.com/en/rest/reference/teams#list-team-members>`_ 317 """ 318 assert role is NotSet or isinstance(role, str), role 319 url_parameters: dict[str, Any] = {} 320 if role is not NotSet: 321 assert role in ["member", "maintainer", "all"] 322 url_parameters["role"] = role 323 return github.PaginatedList.PaginatedList( 324 github.NamedUser.NamedUser, 325 self._requester, 326 f"{self.url}/members", 327 url_parameters, 328 ) 329 330 def get_repos(self) -> PaginatedList[Repository]: 331 """ 332 :calls: `GET /teams/{id}/repos <https://docs.github.com/en/rest/reference/teams>`_ 333 """ 334 return github.PaginatedList.PaginatedList( 335 github.Repository.Repository, self._requester, f"{self.url}/repos", None 336 ) 337 338 def invitations(self) -> PaginatedList[NamedUser]: 339 """ 340 :calls: `GET /teams/{id}/invitations <https://docs.github.com/en/rest/reference/teams#members>`_ 341 """ 342 return github.PaginatedList.PaginatedList( 343 github.NamedUser.NamedUser, 344 self._requester, 345 f"{self.url}/invitations", 346 None, 347 headers={"Accept": Consts.mediaTypeOrganizationInvitationPreview}, 348 ) 349 350 def has_in_members(self, member: NamedUser) -> bool: 351 """ 352 :calls: `GET /teams/{id}/members/{user} <https://docs.github.com/en/rest/reference/teams>`_ 353 """ 354 assert isinstance(member, github.NamedUser.NamedUser), member 355 status, headers, data = self._requester.requestJson("GET", f"{self.url}/members/{member._identity}") 356 return status == 204 357 358 def has_in_repos(self, repo: Repository) -> bool: 359 """ 360 :calls: `GET /teams/{id}/repos/{owner}/{repo} <https://docs.github.com/en/rest/reference/teams>`_ 361 """ 362 assert isinstance(repo, github.Repository.Repository), repo 363 status, headers, data = self._requester.requestJson("GET", f"{self.url}/repos/{repo._identity}") 364 return status == 204 365 366 def remove_membership(self, member: NamedUser) -> None: 367 """ 368 :calls: `DELETE /teams/{team_id}/memberships/{username} <https://docs.github.com/en/rest/reference/teams#remove-team-membership-for-a-user>`_ 369 """ 370 assert isinstance(member, github.NamedUser.NamedUser), member 371 headers, data = self._requester.requestJsonAndCheck("DELETE", f"{self.url}/memberships/{member._identity}") 372 373 def remove_from_members(self, member: NamedUser) -> None: 374 """ 375 This API call is deprecated. Use `remove_membership` instead: 376 https://docs.github.com/en/rest/reference/teams#add-or-update-team-membership-for-a-user-legacy 377 378 :calls: `DELETE /teams/{id}/members/{user} <https://docs.github.com/en/rest/reference/teams>`_ 379 """ 380 assert isinstance(member, github.NamedUser.NamedUser), member 381 headers, data = self._requester.requestJsonAndCheck("DELETE", f"{self.url}/members/{member._identity}") 382 383 def remove_from_repos(self, repo: Repository) -> None: 384 """ 385 :calls: `DELETE /teams/{id}/repos/{owner}/{repo} <https://docs.github.com/en/rest/reference/teams>`_ 386 """ 387 assert isinstance(repo, github.Repository.Repository), repo 388 headers, data = self._requester.requestJsonAndCheck("DELETE", f"{self.url}/repos/{repo._identity}") 389 390 @property 391 def _identity(self) -> int: 392 return self.id 393 394 def _useAttributes(self, attributes: dict[str, Any]) -> None: 395 if "id" in attributes: # pragma no branch 396 self._id = self._makeIntAttribute(attributes["id"]) 397 if "members_count" in attributes: # pragma no branch 398 self._members_count = self._makeIntAttribute(attributes["members_count"]) 399 if "members_url" in attributes: # pragma no branch 400 self._members_url = self._makeStringAttribute(attributes["members_url"]) 401 if "name" in attributes: # pragma no branch 402 self._name = self._makeStringAttribute(attributes["name"]) 403 if "description" in attributes: # pragma no branch 404 self._description = self._makeStringAttribute(attributes["description"]) 405 if "permission" in attributes: # pragma no branch 406 self._permission = self._makeStringAttribute(attributes["permission"]) 407 if "repos_count" in attributes: # pragma no branch 408 self._repos_count = self._makeIntAttribute(attributes["repos_count"]) 409 if "repositories_url" in attributes: # pragma no branch 410 self._repositories_url = self._makeStringAttribute(attributes["repositories_url"]) 411 if "slug" in attributes: # pragma no branch 412 self._slug = self._makeStringAttribute(attributes["slug"]) 413 if "url" in attributes: # pragma no branch 414 self._url = self._makeStringAttribute(attributes["url"]) 415 if "organization" in attributes: # pragma no branch 416 self._organization = self._makeClassAttribute(github.Organization.Organization, attributes["organization"]) 417 if "privacy" in attributes: # pragma no branch 418 self._privacy = self._makeStringAttribute(attributes["privacy"]) 419 if "parent" in attributes: # pragma no branch 420 self._parent = self._makeClassAttribute(github.Team.Team, attributes["parent"]) 421 if "html_url" in attributes: 422 self._html_url = self._makeStringAttribute(attributes["html_url"])