From edab176b5d128953397d5c15a254098565eeafaf Mon Sep 17 00:00:00 2001 From: Shibasis Patel Date: Fri, 28 Jun 2019 09:05:58 +0530 Subject: [PATCH] Adding support for pending team invitations (#993) * fixes issue#823 * Update GithubObject.py * Added tests for Pending team invitations(Issue#823) Co-authored-by: Wan Liuyang --- github/NamedUser.py | 43 +++++++++++++++++ github/Organization.py | 15 ++++++ github/Team.py | 16 +++++++ tests/AllTests.py | 1 + tests/Issue823.py | 47 +++++++++++++++++++ tests/ReplayData/Issue823.setUp.txt | 22 +++++++++ ...823.testGetPendingInvitationAttributes.txt | 11 +++++ 7 files changed, 155 insertions(+) create mode 100644 tests/Issue823.py create mode 100644 tests/ReplayData/Issue823.setUp.txt create mode 100644 tests/ReplayData/Issue823.testGetPendingInvitationAttributes.txt diff --git a/github/NamedUser.py b/github/NamedUser.py index 91c8524e..b1a1af22 100644 --- a/github/NamedUser.py +++ b/github/NamedUser.py @@ -225,6 +225,22 @@ class NamedUser(github.GithubObject.CompletableGithubObject): self._completeIfNotSet(self._id) return self._id.value + @property + def invitation_teams_url(self): + """ + :type: string + """ + self._completeIfNotSet(self._invitation_teams_url) + return self._invitation_teams_url.value + + @property + def inviter(self): + """ + :type: github.NamedUser.NamedUser + """ + self._completeIfNotSet(self._inviter) + return self._inviter.value + @property def location(self): """ @@ -320,6 +336,13 @@ class NamedUser(github.GithubObject.CompletableGithubObject): """ self._completeIfNotSet(self._repos_url) return self._repos_url.value + @property + def role(self): + """ + :type: string + """ + self._completeIfNotSet(self._role) + return self._role.value @property def site_admin(self): @@ -353,6 +376,14 @@ class NamedUser(github.GithubObject.CompletableGithubObject): self._completeIfNotSet(self._suspended_at) return self._suspended_at.value + @property + def team_count(self): + """ + :type: integer + """ + self._completeIfNotSet(self._team_count) + return self._team_count.value + @property def total_private_repos(self): """ @@ -610,6 +641,8 @@ class NamedUser(github.GithubObject.CompletableGithubObject): self._hireable = github.GithubObject.NotSet self._html_url = github.GithubObject.NotSet self._id = github.GithubObject.NotSet + self._invitation_teams_url = github.GithubObject.NotSet + self._inviter = github.GithubObject.NotSet self._location = github.GithubObject.NotSet self._login = github.GithubObject.NotSet self._name = github.GithubObject.NotSet @@ -623,10 +656,12 @@ class NamedUser(github.GithubObject.CompletableGithubObject): self._public_repos = github.GithubObject.NotSet self._received_events_url = github.GithubObject.NotSet self._repos_url = github.GithubObject.NotSet + self._role = github.GithubObject.NotSet self._site_admin = github.GithubObject.NotSet self._starred_url = github.GithubObject.NotSet self._subscriptions_url = github.GithubObject.NotSet self._suspended_at = github.GithubObject.NotSet + self._team_count = github.GithubObject.NotSet self._total_private_repos = github.GithubObject.NotSet self._type = github.GithubObject.NotSet self._updated_at = github.GithubObject.NotSet @@ -671,6 +706,10 @@ class NamedUser(github.GithubObject.CompletableGithubObject): self._html_url = self._makeStringAttribute(attributes["html_url"]) if "id" in attributes: # pragma no branch self._id = self._makeIntAttribute(attributes["id"]) + if "invitation_teams_url" in attributes: # pragma no branch + self._invitation_teams_url = self._makeStringAttribute(attributes["invitation_teams_url"]) + if "inviter" in attributes: # pragma no branch + self._inviter = self._makeClassAttribute(github.NamedUser.NamedUser, attributes["inviter"]) if "location" in attributes: # pragma no branch self._location = self._makeStringAttribute(attributes["location"]) if "login" in attributes: # pragma no branch @@ -697,6 +736,8 @@ class NamedUser(github.GithubObject.CompletableGithubObject): self._received_events_url = self._makeStringAttribute(attributes["received_events_url"]) if "repos_url" in attributes: # pragma no branch self._repos_url = self._makeStringAttribute(attributes["repos_url"]) + if "role" in attributes: # pragma no branch + self._role = self._makeStringAttribute(attributes["role"]) if "site_admin" in attributes: # pragma no branch self._site_admin = self._makeBoolAttribute(attributes["site_admin"]) if "starred_url" in attributes: # pragma no branch @@ -705,6 +746,8 @@ class NamedUser(github.GithubObject.CompletableGithubObject): self._subscriptions_url = self._makeStringAttribute(attributes["subscriptions_url"]) if "suspended_at" in attributes: # pragma no branch self._suspended_at = self._makeDatetimeAttribute(attributes["suspended_at"]) + if "team_count" in attributes: + self._team_count = self._makeIntAttribute(attributes["team_count"]) if "total_private_repos" in attributes: # pragma no branch self._total_private_repos = self._makeIntAttribute(attributes["total_private_repos"]) if "type" in attributes: # pragma no branch diff --git a/github/Organization.py b/github/Organization.py index dd1895da..d415f214 100644 --- a/github/Organization.py +++ b/github/Organization.py @@ -824,6 +824,21 @@ class Organization(github.GithubObject.CompletableGithubObject): None ) + def invitations(self): + """ + :calls: `GET /orgs/:org/invitations `_ + :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.NamedUser.NamedUser` + """ + return github.PaginatedList.PaginatedList( + github.NamedUser.NamedUser, + self._requester, + self.url + "/invitations", + None, + headers={ + "Accept": Consts.mediaTypeOrganizationInvitationPreview + } + ) + def invite_user(self, user=github.GithubObject.NotSet, email=github.GithubObject.NotSet, role=github.GithubObject.NotSet, teams=github.GithubObject.NotSet): """ :calls: `POST /orgs/:org/invitations `_ diff --git a/github/Team.py b/github/Team.py index 81c96bd3..f3bd255c 100644 --- a/github/Team.py +++ b/github/Team.py @@ -48,6 +48,7 @@ import github.Repository import github.NamedUser import github.Organization +import Consts class Team(github.GithubObject.CompletableGithubObject): """ @@ -291,6 +292,21 @@ class Team(github.GithubObject.CompletableGithubObject): None ) + def invitations(self): + """ + :calls: `GET /teams/:id/invitations `_ + :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.NamedUser.NamedUser` + """ + return github.PaginatedList.PaginatedList( + github.NamedUser.NamedUser, + self._requester, + self.url + "/invitations", + None, + headers={ + "Accept": Consts.mediaTypeOrganizationInvitationPreview + } + ) + def has_in_members(self, member): """ :calls: `GET /teams/:id/members/:user `_ diff --git a/tests/AllTests.py b/tests/AllTests.py index 9122d225..98bbb2e5 100644 --- a/tests/AllTests.py +++ b/tests/AllTests.py @@ -126,3 +126,4 @@ from Issue494 import * from Issue572 import * from Issue937 import * from Issue945 import * +from Issue823 import * \ No newline at end of file diff --git a/tests/Issue823.py b/tests/Issue823.py new file mode 100644 index 00000000..1e2442bc --- /dev/null +++ b/tests/Issue823.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- + +############################ Copyrights and license ############################ +# # +# Copyright 2013 Vincent Jacques # +# Copyright 2014 Vincent Jacques # +# Copyright 2016 Peter Buckley # +# Copyright 2018 sfdye # +# # +# This file is part of PyGithub. # +# http://pygithub.readthedocs.io/ # +# # +# PyGithub is free software: you can redistribute it and/or modify it under # +# the terms of the GNU Lesser General Public License as published by the Free # +# Software Foundation, either version 3 of the License, or (at your option) # +# any later version. # +# # +# PyGithub is distributed in the hope that it will be useful, but WITHOUT ANY # +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # +# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # +# details. # +# # +# You should have received a copy of the GNU Lesser General Public License # +# along with PyGithub. If not, see . # +# # +################################################################################ + +import Framework +import github + + +class Issue823(Framework.TestCase): + def setUp(self): + Framework.TestCase.setUp(self) + self.org = self.g.get_organization('p-society') + self.team = self.org.get_team(2745783) + self.pending_invitations = self.team.invitations() + + def testGetPendingInvitationAttributes(self): + team_url = self.pending_invitations[0].invitation_teams_url + self.assertEqual(team_url, 'https://api.github.com/organizations/29895434/invitations/6080804/teams') + inviter = self.pending_invitations[0].inviter.login + self.assertEqual(inviter,'palash25' ) + role = self.pending_invitations[0].role + self.assertEqual(role, 'direct_member') + team_count = self.pending_invitations[0].team_count + self.assertEqual(team_count, 1) diff --git a/tests/ReplayData/Issue823.setUp.txt b/tests/ReplayData/Issue823.setUp.txt new file mode 100644 index 00000000..360c0707 --- /dev/null +++ b/tests/ReplayData/Issue823.setUp.txt @@ -0,0 +1,22 @@ +https +GET +api.github.com +None +/orgs/p-society +{'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python'} +None +200 +[('Server', 'GitHub.com'), ('Date', 'Wed, 26 Dec 2018 11:10:55 GMT'), ('Content-Type', 'application/json; charset=utf-8'), ('Transfer-Encoding', 'chunked'), ('Status', '200 OK'), ('X-RateLimit-Limit', '5000'), ('X-RateLimit-Remaining', '4999'), ('X-RateLimit-Reset', '1545826255'), ('Cache-Control', 'private, max-age=60, s-maxage=60'), ('Vary', 'Accept, Authorization, Cookie, X-GitHub-OTP'), ('ETag', 'W/"6580793c7561d3bf848eb8a135d33833"'), ('Last-Modified', 'Wed, 05 Dec 2018 16:45:38 GMT'), ('X-GitHub-Media-Type', 'github.v3; format=json'), ('Access-Control-Expose-Headers', 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type'), ('Access-Control-Allow-Origin', '*'), ('Strict-Transport-Security', 'max-age=31536000; includeSubdomains; preload'), ('X-Frame-Options', 'deny'), ('X-Content-Type-Options', 'nosniff'), ('X-XSS-Protection', '1; mode=block'), ('Referrer-Policy', 'origin-when-cross-origin, strict-origin-when-cross-origin'), ('Content-Security-Policy', "default-src 'none'"), ('Content-Encoding', 'gzip'), ('X-GitHub-Request-Id', '8C70:6FB5:4C1D7B3:A5203B7:5C2361BD')] +{"login":"p-society","id":29895434,"node_id":"MDEyOk9yZ2FuaXphdGlvbjI5ODk1NDM0","url":"https://api.github.com/orgs/p-society","repos_url":"https://api.github.com/orgs/p-society/repos","events_url":"https://api.github.com/orgs/p-society/events","hooks_url":"https://api.github.com/orgs/p-society/hooks","issues_url":"https://api.github.com/orgs/p-society/issues","members_url":"https://api.github.com/orgs/p-society/members{/member}","public_members_url":"https://api.github.com/orgs/p-society/public_members{/member}","avatar_url":"https://avatars0.githubusercontent.com/u/29895434?v=4","description":"IIIT Bhubaneswar's Programming Society","name":"Programming Society","company":null,"blog":"https://p-society.herokuapp.com/","location":"Bhubaneswar","email":"psocietyiiitbh@gmail.com","is_verified":false,"has_organization_projects":true,"has_repository_projects":true,"public_repos":23,"public_gists":0,"followers":0,"following":0,"html_url":"https://github.com/p-society","created_at":"2017-07-04T14:47:55Z","updated_at":"2018-12-05T16:45:38Z","type":"Organization","total_private_repos":2,"owned_private_repos":2,"private_gists":0,"disk_usage":30533,"collaborators":0,"billing_email":"shibasishpatel@live.in","plan":{"name":"team","space":976562499,"private_repos":99999,"filled_seats":34,"seats":34},"default_repository_permission":"read","members_can_create_repositories":true,"two_factor_requirement_enabled":false} + +https +GET +api.github.com +None +/teams/2745783 +{'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python'} +None +200 +[('Server', 'GitHub.com'), ('Date', 'Wed, 26 Dec 2018 11:10:57 GMT'), ('Content-Type', 'application/json; charset=utf-8'), ('Transfer-Encoding', 'chunked'), ('Status', '200 OK'), ('X-RateLimit-Limit', '5000'), ('X-RateLimit-Remaining', '4998'), ('X-RateLimit-Reset', '1545826255'), ('Cache-Control', 'private, max-age=60, s-maxage=60'), ('Vary', 'Accept, Authorization, Cookie, X-GitHub-OTP'), ('ETag', 'W/"0452ef7aded17dfd16179e5248c4bd14"'), ('Last-Modified', 'Tue, 01 May 2018 17:57:43 GMT'), ('X-GitHub-Media-Type', 'github.v3; format=json'), ('Access-Control-Expose-Headers', 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type'), ('Access-Control-Allow-Origin', '*'), ('Strict-Transport-Security', 'max-age=31536000; includeSubdomains; preload'), ('X-Frame-Options', 'deny'), ('X-Content-Type-Options', 'nosniff'), ('X-XSS-Protection', '1; mode=block'), ('Referrer-Policy', 'origin-when-cross-origin, strict-origin-when-cross-origin'), ('Content-Security-Policy', "default-src 'none'"), ('Content-Encoding', 'gzip'), ('X-GitHub-Request-Id', 'D55C:6FB2:118318A:2F5B512:5C2361C0')] +{"name":"2019-batch","id":2745783,"node_id":"MDQ6VGVhbTI3NDU3ODM=","slug":"2019-batch","description":"Students from the batch of 2015-19","privacy":"closed","url":"https://api.github.com/teams/2745783","members_url":"https://api.github.com/teams/2745783/members{/member}","repositories_url":"https://api.github.com/teams/2745783/repos","permission":"pull","created_at":"2018-05-01T17:57:43Z","updated_at":"2018-05-01T17:57:43Z","members_count":6,"repos_count":1,"organization":{"login":"p-society","id":29895434,"node_id":"MDEyOk9yZ2FuaXphdGlvbjI5ODk1NDM0","url":"https://api.github.com/orgs/p-society","repos_url":"https://api.github.com/orgs/p-society/repos","events_url":"https://api.github.com/orgs/p-society/events","hooks_url":"https://api.github.com/orgs/p-society/hooks","issues_url":"https://api.github.com/orgs/p-society/issues","members_url":"https://api.github.com/orgs/p-society/members{/member}","public_members_url":"https://api.github.com/orgs/p-society/public_members{/member}","avatar_url":"https://avatars0.githubusercontent.com/u/29895434?v=4","description":"IIIT Bhubaneswar's Programming Society","name":"Programming Society","company":null,"blog":"https://p-society.herokuapp.com/","location":"Bhubaneswar","email":"psocietyiiitbh@gmail.com","is_verified":false,"has_organization_projects":true,"has_repository_projects":true,"public_repos":23,"public_gists":0,"followers":0,"following":0,"html_url":"https://github.com/p-society","created_at":"2017-07-04T14:47:55Z","updated_at":"2018-12-05T16:45:38Z","type":"Organization"}} + diff --git a/tests/ReplayData/Issue823.testGetPendingInvitationAttributes.txt b/tests/ReplayData/Issue823.testGetPendingInvitationAttributes.txt new file mode 100644 index 00000000..f5e7e823 --- /dev/null +++ b/tests/ReplayData/Issue823.testGetPendingInvitationAttributes.txt @@ -0,0 +1,11 @@ +https +GET +api.github.com +None +/teams/2745783/invitations +{'Accept': 'application/vnd.github.dazzler-preview+json', 'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python'} +None +200 +[('Server', 'GitHub.com'), ('Date', 'Wed, 26 Dec 2018 11:10:59 GMT'), ('Content-Type', 'application/json; charset=utf-8'), ('Transfer-Encoding', 'chunked'), ('Status', '200 OK'), ('X-RateLimit-Limit', '5000'), ('X-RateLimit-Remaining', '4997'), ('X-RateLimit-Reset', '1545826255'), ('Cache-Control', 'private, max-age=60, s-maxage=60'), ('Vary', 'Accept, Authorization, Cookie, X-GitHub-OTP'), ('ETag', 'W/"debc51b3da093a55c674f2303663ab69"'), ('X-GitHub-Media-Type', 'github.dazzler-preview; format=json'), ('Access-Control-Expose-Headers', 'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type'), ('Access-Control-Allow-Origin', '*'), ('Strict-Transport-Security', 'max-age=31536000; includeSubdomains; preload'), ('X-Frame-Options', 'deny'), ('X-Content-Type-Options', 'nosniff'), ('X-XSS-Protection', '1; mode=block'), ('Referrer-Policy', 'origin-when-cross-origin, strict-origin-when-cross-origin'), ('Content-Security-Policy', "default-src 'none'"), ('Content-Encoding', 'gzip'), ('X-GitHub-Request-Id', '8C74:6FB4:3A8ABED:8659024:5C2361C2')] +[{"id":6080804,"node_id":"MDIyOk9yZ2FuaXphdGlvbkludml0YXRpb242MDgwODA0","login":"pratchef","email":null,"role":"direct_member","created_at":"2018-05-02T00:19:26.000+05:30","inviter":{"login":"palash25","id":21367710,"node_id":"MDQ6VXNlcjIxMzY3NzEw","avatar_url":"https://avatars0.githubusercontent.com/u/21367710?v=4","gravatar_id":"","url":"https://api.github.com/users/palash25","html_url":"https://github.com/palash25","followers_url":"https://api.github.com/users/palash25/followers","following_url":"https://api.github.com/users/palash25/following{/other_user}","gists_url":"https://api.github.com/users/palash25/gists{/gist_id}","starred_url":"https://api.github.com/users/palash25/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/palash25/subscriptions","organizations_url":"https://api.github.com/users/palash25/orgs","repos_url":"https://api.github.com/users/palash25/repos","events_url":"https://api.github.com/users/palash25/events{/privacy}","received_events_url":"https://api.github.com/users/palash25/received_events","type":"User","site_admin":false},"team_count":1,"invitation_teams_url":"https://api.github.com/organizations/29895434/invitations/6080804/teams"}] +