mirror of
https://github.com/status-im/PyGithub.git
synced 2026-08-31 19:01:15 +00:00
Implement Organization.invite_user (#880)
Utilize the API preview for the organization invite system to allow
organization owners to invite outside colloborators either via passing
in the user directly, or their email address.
Fixes #851
This commit is contained in:
committed by
Wan Liuyang
parent
ef16702a0d
commit
eb80564b01
@@ -81,5 +81,8 @@ mediaTypeLabelDescriptionSearchPreview = "application/vnd.github.symmetra-previe
|
||||
# https://developer.github.com/changes/2018-01-10-lock-reason-api-preview/
|
||||
mediaTypeLockReasonPreview = "application/vnd.github.sailor-v-preview+json"
|
||||
|
||||
# https://developer.github.com/changes/2018-01-25-organization-invitation-api-preview/
|
||||
mediaTypeOrganizationInvitationPreview = "application/vnd.github.dazzler-preview+json"
|
||||
|
||||
# https://developer.github.com/changes/2018-03-16-protected-branches-required-approving-reviews/
|
||||
mediaTypeRequireMultipleApprovingReviews = "application/vnd.github.luke-cage-preview+json"
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
# Copyright 2018 Raihaan <31362124+res0nance@users.noreply.github.com> #
|
||||
# Copyright 2018 Tim Boring <tboring@hearst.com> #
|
||||
# Copyright 2018 sfdye <tsfdye@gmail.com> #
|
||||
# Copyright 2018 Steve Kowalik <steven@wedontsleep.org> #
|
||||
# #
|
||||
# This file is part of PyGithub. #
|
||||
# http://pygithub.readthedocs.io/ #
|
||||
@@ -52,6 +53,7 @@ import github.Event
|
||||
import github.Repository
|
||||
import github.NamedUser
|
||||
|
||||
import Consts
|
||||
|
||||
class Organization(github.GithubObject.CompletableGithubObject):
|
||||
"""
|
||||
@@ -776,6 +778,37 @@ class Organization(github.GithubObject.CompletableGithubObject):
|
||||
None
|
||||
)
|
||||
|
||||
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 <http://developer.github.com/v3/orgs/members>`_
|
||||
:param user: :class:`github.NamedUser.NamedUser`
|
||||
:param email: string
|
||||
:param role: string
|
||||
:param teams: array of :class:`github.Team.Team`
|
||||
:rtype: None
|
||||
"""
|
||||
assert user is github.GithubObject.NotSet or isinstance(user, github.NamedUser.NamedUser), user
|
||||
assert email is github.GithubObject.NotSet or isinstance(email, (str, unicode)), email
|
||||
assert (email is github.GithubObject.NotSet) ^ (user is github.GithubObject.NotSet), "specify only one of email or user"
|
||||
parameters = {}
|
||||
if user is not github.GithubObject.NotSet:
|
||||
parameters["invitee_id"] = user.id
|
||||
elif email is not github.GithubObject.NotSet:
|
||||
parameters["email"] = email
|
||||
if role is not github.GithubObject.NotSet:
|
||||
assert isinstance(role, (str, unicode)), role
|
||||
assert role in ['admin', 'direct_member', 'billing_manager']
|
||||
parameters["role"] = role
|
||||
if teams is not github.GithubObject.NotSet:
|
||||
assert all(isinstance(team, github.Team.Team) for team in teams)
|
||||
parameters["team_ids"] = [t.id for t in teams]
|
||||
headers, data = self._requester.requestJsonAndCheck(
|
||||
"POST",
|
||||
self.url + "/invitations",
|
||||
headers={'Accept': Consts.mediaTypeOrganizationInvitationPreview},
|
||||
input=parameters
|
||||
)
|
||||
|
||||
def has_in_members(self, member):
|
||||
"""
|
||||
:calls: `GET /orgs/:org/members/:user <http://developer.github.com/v3/orgs/members>`_
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
# #
|
||||
################################################################################
|
||||
|
||||
import github
|
||||
|
||||
import Framework
|
||||
|
||||
import datetime
|
||||
@@ -213,3 +215,35 @@ class Organization(Framework.TestCase):
|
||||
pygithub = self.g.get_user("jacquev6").get_repo("PyGithub")
|
||||
repo = self.org.create_fork(pygithub)
|
||||
self.assertEqual(repo.url, "https://api.github.com/repos/BeaverSoftware/PyGithub")
|
||||
|
||||
def testInviteUserWithNeither(self):
|
||||
with self.assertRaises(AssertionError) as raisedexp:
|
||||
self.org.invite_user()
|
||||
self.assertEqual("specify only one of email or user", str(raisedexp.exception))
|
||||
|
||||
def testInviteUserWithBoth(self):
|
||||
jacquev6 = self.g.get_user('jacquev6')
|
||||
with self.assertRaises(AssertionError) as raisedexp:
|
||||
self.org.invite_user(email='foo', user=jacquev6)
|
||||
self.assertEqual("specify only one of email or user", str(raisedexp.exception))
|
||||
|
||||
def testInviteUserByName(self):
|
||||
jacquev6 = self.g.get_user('jacquev6')
|
||||
self.org.invite_user(user=jacquev6)
|
||||
|
||||
def testInviteUserByEmail(self):
|
||||
self.org.invite_user(email='foo@example.com')
|
||||
|
||||
def testInviteUserWithRoleAndTeam(self):
|
||||
team = self.org.create_team("Team created by PyGithub")
|
||||
self.org.invite_user(email='foo@example.com', role='billing_manager', teams=[team])
|
||||
|
||||
def testInviteUserAsNonOwner(self):
|
||||
with self.assertRaises(github.GithubException) as raisedexp:
|
||||
self.org.invite_user(email='bar@example.com')
|
||||
self.assertEqual(raisedexp.exception.status, 403)
|
||||
self.assertEqual(raisedexp.exception.data, {
|
||||
u'documentation_url': u'https://developer.github.com/v3/orgs/members/#create-organization-invitation',
|
||||
u'message': u'You must be an admin to create an invitation to an organization.'
|
||||
}
|
||||
)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
https
|
||||
POST
|
||||
api.github.com
|
||||
None
|
||||
/orgs/BeaverSoftware/invitations
|
||||
{'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python', 'Content-Type': 'application/json', 'Accept': 'application/vnd.github.dazzler-preview+json'}
|
||||
{"email": "bar@example.com"}
|
||||
403
|
||||
[('status', '403 Forbidden'), ('x-ratelimit-remaining', '4980'), ('content-length', '37'), ('server', 'nginx/1.0.13'), ('connection', 'keep-alive'), ('x-ratelimit-limit', '5000'), ('etag', '"fb43ecb6e2f75e3940aa8e9edc5ed691"'), ('date', 'Sun, 26 Aug 2018 01:19:54 GMT'), ('content-type', 'application/json; charset=utf-8')]
|
||||
{"documentation_url": "https://developer.github.com/v3/orgs/members/#create-organization-invitation", "message": "You must be an admin to create an invitation to an organization."}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
https
|
||||
POST
|
||||
api.github.com
|
||||
None
|
||||
/orgs/BeaverSoftware/invitations
|
||||
{'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python', 'Content-Type': 'application/json', 'Accept': 'application/vnd.github.dazzler-preview+json'}
|
||||
{"email": "foo@example.com"}
|
||||
201
|
||||
[('content-length', '1273'), ('x-runtime-rack', '0.071768'), ('vary', 'Accept, Authorization, Cookie, X-GitHub-OTP, Accept-Encoding'), ('x-oauth-scopes', 'admin:gpg_key, admin:org, admin:org_hook, admin:public_key, admin:repo_hook, delete_repo, gist, notifications, repo, user, write:discussion'), ('x-xss-protection', '1; mode=block'), ('x-content-type-options', 'nosniff'), ('x-accepted-oauth-scopes', 'admin:org'), ('etag', '"a7e7e69aa42ed0d2132dbfcaa032dbf7"'), ('cache-control', 'private, max-age=60, s-maxage=60'), ('referrer-policy', 'origin-when-cross-origin, strict-origin-when-cross-origin'), ('status', '201 Created'), ('x-ratelimit-remaining', '4998'), ('x-github-media-type', 'github.dazzler-preview; format=json'), ('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'), ('x-github-request-id', 'A542:171F:1945733:1F92438:5B822DC1'), ('date', 'Sun, 26 Aug 2018 04:34:24 GMT'), ('access-control-allow-origin', '*'), ('content-security-policy', "default-src 'none'"), ('strict-transport-security', 'max-age=31536000; includeSubdomains; preload'), ('server', 'GitHub.com'), ('x-ratelimit-limit', '5000'), ('x-frame-options', 'deny'), ('content-type', 'application/json; charset=utf-8'), ('x-ratelimit-reset', '1535261649')]
|
||||
{"created_at":"2018-08-26T04:15:26Z","email":"foo@example.com","invitation_teams_url":"https://api.github.com/organizations/42690536/invitations/12423419/teams","node_id":"MDIyOk9yZ2FuaXphdGlvbkludml0YXRpb24xMjQyMzQxOQ==","role":"direct_member","team_count":0,"login":null,"inviter":{"following_url":"https://api.github.com/users/jacquev6/following{/other_user}","events_url":"https://api.github.com/users/jacquev6/events{/privacy}","avatar_url":"https://avatars0.githubusercontent.com/u/327146?v=4","url":"https://api.github.com/users/jacquev6","gists_url":"https://api.github.com/users/jacquev6/gists{/gist_id}","html_url":"https://github.com/jacquev6","subscriptions_url":"https://api.github.com/users/jacquev6/subscriptions","node_id":"MDQ6VXNlcjE1MjI1MDU5","repos_url":"https://api.github.com/users/jacquev6/repos","received_events_url":"https://api.github.com/users/jacquev6/received_events","gravatar_id":"","starred_url":"https://api.github.com/users/jacquev6/starred{/owner}{/repo}","site_admin":false,"login":"jacquev6","type":"User","id":327146,"followers_url":"https://api.github.com/users/jacquev6/followers","organizations_url":"https://api.github.com/users/jacquev6/orgs"},"id":12423419}
|
||||
@@ -0,0 +1,21 @@
|
||||
https
|
||||
GET
|
||||
api.github.com
|
||||
None
|
||||
/users/jacquev6
|
||||
{'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python'}
|
||||
None
|
||||
200
|
||||
[('status', '200 OK'), ('x-ratelimit-remaining', '4962'), ('content-length', '801'), ('server', 'nginx/1.0.13'), ('connection', 'keep-alive'), ('x-ratelimit-limit', '5000'), ('etag', '"fc78d67f262cad756e42354c78ecea4e"'), ('date', 'Tue, 28 Aug 2018 00:16:42 GMT'), ('content-type', 'application/json; charset=utf-8')]
|
||||
{"public_repos":11,"type":"User","disk_usage":17080,"hireable":false,"blog":"http://vincent-jacques.net","url":"https://api.github.com/users/jacquev6","bio":"","plan":{"collaborators":1,"private_repos":5,"name":"micro","space":614400},"avatar_url":"https://secure.gravatar.com/avatar/b68de5ae38616c296fa345d2b9df2225?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","total_private_repos":5,"public_gists":2,"company":"Criteo","gravatar_id":"b68de5ae38616c296fa345d2b9df2225","login":"jacquev6","owned_private_repos":5,"private_gists":5,"collaborators":0,"email":"vincent@vincent-jacques.net","followers":13,"name":"Vincent Jacques","created_at":"2010-07-09T06:10:06Z","location":"Paris, France","id":327146,"following":24,"html_url":"https://github.com/jacquev6"}
|
||||
|
||||
https
|
||||
POST
|
||||
api.github.com
|
||||
None
|
||||
/orgs/BeaverSoftware/invitations
|
||||
{'Authorization': 'Basic login_and_password_removed', 'Content-Type': 'application/json', 'User-Agent': 'PyGithub/Python', 'Accept': 'application/vnd.github.dazzler-preview+json'}
|
||||
{"invitee_id": 327146}
|
||||
201
|
||||
[('content-length', '1275'), ('x-runtime-rack', '0.117213'), ('vary', 'Accept, Authorization, Cookie, X-GitHub-OTP, Accept-Encoding'), ('x-oauth-scopes', 'admin:gpg_key, admin:org, admin:org_hook, admin:public_key, admin:repo_hook, delete_repo, gist, notifications, repo, user, write:discussion'), ('x-xss-protection', '1; mode=block'), ('x-content-type-options', 'nosniff'), ('x-accepted-oauth-scopes', 'admin:org'), ('etag', '"25cb4d8e0275889661b12ee12785894c"'), ('cache-control', 'private, max-age=60, s-maxage=60'), ('referrer-policy', 'origin-when-cross-origin, strict-origin-when-cross-origin'), ('status', '201 Created'), ('x-ratelimit-remaining', '4990'), ('x-github-media-type', 'github.dazzler-preview; format=json'), ('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'), ('x-github-request-id', 'EA96:5917:ED3378:133B5C8:5B849484'), ('date', 'Tue, 28 Aug 2018 00:17:45 GMT'), ('access-control-allow-origin', '*'), ('content-security-policy', "default-src 'none'"), ('strict-transport-security', 'max-age=31536000; includeSubdomains; preload'), ('server', 'GitHub.com'), ('x-ratelimit-limit', '5000'), ('x-frame-options', 'deny'), ('content-type', 'application/json; charset=utf-8'), ('x-ratelimit-reset', '1535417380')]
|
||||
{"created_at":"2018-08-28T00:17:45Z","email":"vincent@vincent-jacques.net","invitation_teams_url":"https://api.github.com/organizations/42733195/invitations/12434153/teams","node_id":"MDIyOk9yZ2FuaXphdGlvbkludml0YXRpb24xMjQzNDE1Mw==","role":"direct_member","team_count":0,"login":"jacquev6","inviter":{"following_url":"https://api.github.com/users/jacquev6/following{/other_user}","events_url":"https://api.github.com/users/jacquev6/events{/privacy}","avatar_url":"https://avatars0.githubusercontent.com/u/327146?v=4","url":"https://api.github.com/users/jacquev6","gists_url":"https://api.github.com/users/jacquev6/gists{/gist_id}","html_url":"https://github.com/jacquev6","subscriptions_url":"https://api.github.com/users/jacquev6/subscriptions","node_id":"MDQ6VXNlcjE1MjI1MDU5","repos_url":"https://api.github.com/users/jacquev6/repos","received_events_url":"https://api.github.com/users/jacquev6/received_events","gravatar_id":"","starred_url":"https://api.github.com/users/jacquev6/starred{/owner}{/repo}","site_admin":false,"login":"jacquev6","type":"User","id":327146,"followers_url":"https://api.github.com/users/jacquev6/followers","organizations_url":"https://api.github.com/users/jacquev6/orgs"},"id":12434153}
|
||||
@@ -0,0 +1,10 @@
|
||||
https
|
||||
GET
|
||||
api.github.com
|
||||
None
|
||||
/users/jacquev6
|
||||
{'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python'}
|
||||
None
|
||||
200
|
||||
[('status', '200 OK'), ('x-ratelimit-remaining', '4962'), ('content-length', '801'), ('server', 'nginx/1.0.13'), ('connection', 'keep-alive'), ('x-ratelimit-limit', '5000'), ('etag', '"fc78d67f262cad756e42354c78ecea4e"'), ('date', 'Tue, 28 Aug 2018 00:16:42 GMT'), ('content-type', 'application/json; charset=utf-8')]
|
||||
{"public_repos":11,"type":"User","disk_usage":17080,"hireable":false,"blog":"http://vincent-jacques.net","url":"https://api.github.com/users/jacquev6","bio":"","plan":{"collaborators":1,"private_repos":5,"name":"micro","space":614400},"avatar_url":"https://secure.gravatar.com/avatar/b68de5ae38616c296fa345d2b9df2225?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png","total_private_repos":5,"public_gists":2,"company":"Criteo","gravatar_id":"b68de5ae38616c296fa345d2b9df2225","login":"jacquev6","owned_private_repos":5,"private_gists":5,"collaborators":0,"email":"vincent@vincent-jacques.net","followers":13,"name":"Vincent Jacques","created_at":"2010-07-09T06:10:06Z","location":"Paris, France","id":327146,"following":24,"html_url":"https://github.com/jacquev6"}
|
||||
@@ -0,0 +1,21 @@
|
||||
https
|
||||
POST
|
||||
api.github.com
|
||||
None
|
||||
/orgs/BeaverSoftware/teams
|
||||
{'Content-Type': 'application/json', 'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python'}
|
||||
{"name": "Team created by PyGithub"}
|
||||
201
|
||||
[('status', '201 Created'), ('x-ratelimit-remaining', '4988'), ('content-length', '145'), ('server', 'nginx/1.0.13'), ('connection', 'keep-alive'), ('x-ratelimit-limit', '5000'), ('etag', '"189a318993cde3e040f2efb4f634f8a8"'), ('date', 'Sat, 26 May 2012 20:58:53 GMT'), ('content-type', 'application/json; charset=utf-8'), ('location', 'https://api.github.com/teams/189850')]
|
||||
{"url":"https://api.github.com/teams/189850","members_count":0,"repos_count":0,"name":"Team created by PyGithub","permission":"pull","id":189850}
|
||||
|
||||
https
|
||||
POST
|
||||
api.github.com
|
||||
None
|
||||
/orgs/BeaverSoftware/invitations
|
||||
{'Content-Type': 'application/json', 'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python', 'Accept': 'application/vnd.github.dazzler-preview+json'}
|
||||
{"email": "foo@example.com", "role": "billing_manager", "team_ids": [189850]}
|
||||
201
|
||||
[('content-length', '1278'), ('x-runtime-rack', '0.122927'), ('vary', 'Accept, Authorization, Cookie, X-GitHub-OTP, Accept-Encoding'), ('x-oauth-scopes', 'admin:gpg_key, admin:org, admin:org_hook, admin:public_key, admin:repo_hook, delete_repo, gist, notifications, repo, user, write:discussion'), ('x-xss-protection', '1; mode=block'), ('x-content-type-options', 'nosniff'), ('x-accepted-oauth-scopes', 'admin:org'), ('etag', '"956a9bf38063ecf225e5970155c6022a"'), ('cache-control', 'private, max-age=60, s-maxage=60'), ('referrer-policy', 'origin-when-cross-origin, strict-origin-when-cross-origin'), ('status', '201 Created'), ('x-ratelimit-remaining', '4994'), ('x-github-media-type', 'github.dazzler-preview; format=json'), ('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'), ('x-github-request-id', 'DA68:171E:16B488C:1C7BDFC:5B8232A3'), ('date', 'Sun, 26 Aug 2018 04:55:00 GMT'), ('access-control-allow-origin', '*'), ('content-security-policy', "default-src 'none'"), ('strict-transport-security', 'max-age=31536000; includeSubdomains; preload'), ('server', 'GitHub.com'), ('x-ratelimit-limit', '5000'), ('x-frame-options', 'deny'), ('content-type', 'application/json; charset=utf-8'), ('x-ratelimit-reset', '1535261649')]
|
||||
{"created_at":"2018-08-26T04:55:00Z","email":"foo@example.com","invitation_teams_url":"https://api.github.com/organizations/32695636/invitations/12325953/teams","node_id":"MDIyOk9yZ2FuaXphdGlvbkludml0YXRpb24xMjQyMzQ1Mw==","role":"billing_manager","team_count":1,"login":null,"inviter":{"following_url":"https://api.github.com/users/jacquev6/following{/other_user}","events_url":"https://api.github.com/users/jacquev6/events{/privacy}","avatar_url":"https://avatars0.githubusercontent.com/u/327146?v=4","url":"https://api.github.com/users/jacquev6","gists_url":"https://api.github.com/users/jacquev6/gists{/gist_id}","html_url":"https://github.com/jacquev6","subscriptions_url":"https://api.github.com/users/jacquev6/subscriptions","node_id":"MDQ6VXNlcjE1MjI1MDU5","repos_url":"https://api.github.com/users/jacquev6/repos","received_events_url":"https://api.github.com/users/jacquev6/received_events","gravatar_id":"","starred_url":"https://api.github.com/users/jacquev6/starred{/owner}{/repo}","site_admin":false,"login":"jacquev6","type":"User","id":327146,"followers_url":"https://api.github.com/users/jacquev6/followers","organizations_url":"https://api.github.com/users/jacquev6/orgs"},"id":12423453}
|
||||
Reference in New Issue
Block a user