8000 Add support for repository invitations · sigmavirus24/github3.py@e616115 · GitHub
[go: up one dir, main page]

Skip to content

Commit e616115

Browse files
committed
Add support for repository invitations
Closes #739
1 parent 87af5a1 commit e616115

12 files changed

+427
-0
lines changed

src/github3/repos/invitation.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# -*- coding: utf-8 -*-
2+
"""Invitation related logic."""
3+
from __future__ import unicode_literals
4+
5+
from json import dumps
6+
7+
from .. import models
8+
from .. import users
9+
10+
from ..decorators import requires_auth
11+
12+
13+
class Invitation(models.GitHubCore):
14+
"""Representation of an invitation to collaborate on a repository.
15+
16+
.. attribute:: created_at
17+
18+
A :class:`~datetime.datetime` instance representing the time and date
19+
when this invitation was created.
20+
21+
.. attribute:: html_url
22+
23+
The URL to view this invitation in a browser.
24+
25+
.. attribute:: id
26+
27+
The unique identifier for this invitation.
28+
29+
.. attribute:: invitee
30+
31+
A :class:`~github3.users.ShortUser` representing the user who was
32+
invited to collaborate.
33+
34+
.. attribute:: inviter
35+
36+
A :class:`~github3.users.ShortUser` representing the user who invited
37+
the ``invitee``.
38+
39+
.. attribute:: permissions
40+
41+
The permissions that the ``invitee`` will have on the repository. Valid
42+
values are ``read``, ``write``, and ``admin``.
43+
44+
.. attribute:: repository
45+
46+
A :class:`~github3.repos.ShortRepository` representing the repository
47+
on which the ``invitee` was invited to collaborate.
48+
49+
.. attribute:: url
50+
51+
The API URL that the ``invitee`` can use to respond to the invitation.
52+
Note that the ``inviter`` must use a different URL, not returned by
53+
the API, to update or cancel the invitation.
54+
"""
55+
56+
class_name = 'Invitation'
57+
allowed_permissions = frozenset(['admin', 'read', 'write'])
58+
59+
def _update_attributes(self, invitation):
60+
from . import repo
61+
self.created_at = self._strptime(invitation['created_at'])
62+
self.html_url = invitation['html_url']
63+
self.id = invitation['id']
64+
self.invitee = users.ShortUser(invitation['invitee'], self)
65+
self.inviter = users.ShortUser(invitation['inviter'], self)
66+
self.permissions = invitation['permissions']
67+
self.repository = repo.ShortRepository(invitation['repository'], self)
68+
self.url = invitation['url']
69+
70+
def _repr(self):
71+
return '<Invitation [{0}]>'.format(self.repository.full_name)
72+
73+
@requires_auth
74+
def accept(self):
75+
"""Accept this invitation.
76+
77+
:returns:
78+
True if successful, False otherwise
79+
"""
80+
return self._boolean(self._patch(self.url), 204, 404)
81+
82+
@requires_auth
83+
def decline(self):
84+
"""Decline this invitation.
85+
86+
:returns:
87+
True if successful, False otherwise
88+
"""
89+
return self._boolean(self._delete(self.url), 204, 404)
90+
91+
@requires_auth
92+
def delete(self):
93+
"""Delete this invitation.
94+
95+
:returns:
96+
True if successful, False otherwise
97+
:rtype:
98+
bool
99+
"""
100+
url = self._build_url(
101+
'invitations', self.id, base_url=self.repository.url)
102+
return self._boolean(self._delete(url), 204, 404)
103+
104+
@requires_auth
105+
def update(self, permissions):
106+
"""Update this invitation.
107+
108+
:param str permissions:
109+
(required), the permissions that will be granted by this invitation
110+
once it has been updated. Options: 'admin', 'read', 'write'
111+
:returns:
112+
The updated invitation
113+
:rtype:
114+
:class:`~github3.repos.invitation.Invitation`
115+
"""
116+
if permissions not in self.allowed_permissions:
117+
raise ValueError("'permissions' must be one of {0}".format(
118+
', '.join(sorted(self.allowed_permissions))
119+
))
120+
url = self._build_url(
121+
'invitations', self.id, base_url=self.repository.url)
122+
data = {'permissions': permissions}
123+
json = self._json(self._patch(url, data=dumps(data)), 200)
124+
return self._instance_or_null(Invitation, json)

src/github3/repos/repo.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from . import contents
3535
from . import deployment
3636
from . import hook
37+
from . import invitation
3738
from . import issue_import
3839
from . import pages
3940
from . import release
@@ -1536,6 +1537,23 @@ def import_issue(self, title, body, created_at, assignee=None,
15361537
json = self._json(data, 202)
15371538
return self._instance_or_null(issue_import.ImportedIssue, json)
15381539

1540+
@requires_auth
1541+
def invitations(self, number=-1, etag=None):
1542+
"""Iterate over the invitations to this repository.
1543+
1544+
:param int number:
1545+
(optional), number of invitations to return. Default: -1 returns
1546+
all available invitations
1547+
:param str etag:
1548+
(optional), ETag from a previous request to the same endpoint
1549+
:returns:
1550+
generator of repository invitation objects
1551+
:rtype:
1552+
:class:`~github3.repos.invitation.Invitation`
1553+
"""
1554+
url = self._build_url('invitations', base_url=self._api)
1555+
return self._iter(int(number), url, invitation.Invitation, etag=etag)
1556+
15391557
def is_assignee(self, username):
15401558
"""Check if the user can be assigned an issue on this repository.
15411559

src/github3/users.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,23 @@ def _update_attributes(self, user):
742742
if self.plan is not None:
743743
self.plan = Plan(self.plan, self)
744744

745+
def repository_invitations(self, number=-1, etag=None):
746+
"""Iterate over the repository invitations for the current user.
747+
748+
:param int number:
749+
(optional), number of invitations to return. Default: -1 returns
750+
all available invitations
751+
:param str etag:
752+
(optional), ETag from a previous request to the same endpoint
753+
:returns:
754+
generator of repository invitation objects
755+
:rtype:
756+
:class:`~github3.repos.invitation.Invitation`
757+
"""
758+
from .repos.invitation import Invitation
759+
url = self._build_url('user', 'repository_invitations')
760+
return self._iter(int(number), url, Invitation, etag=etag)
761+
745762

746763
class Collaborator(_User):
747764
"""Object for the representation of a collaborator.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"http_interactions": [{"request": {"body": {"string": "", "encoding": "utf-8"}, "headers": {"Accept-Encoding": ["gzip, deflate"], "Accept": ["application/vnd.github.v3.full+json"], "User-Agent": ["github3.py/1.1.0"], "Accept-Charset": ["utf-8"], "Connection": ["keep-alive"], "Content-Type": ["application/json"], "Authorization": ["token <AUTH_TOKEN>"]}, "method": "GET", "uri": "https://api.github.com/repos/jacquerie/flask-shell-bpython"}, "response": {"body": {"string": "", "base64_string": "H4sIAAAAAAAAA62Y227jNhCGX8XwbRPLds4Ggm2LLhYp6t124W0XuTFoibYYU6RKUnZtIe/enwdbspFNlEQ3iaJwPv4azYxmWHZZ0h1dDi6uhjfDq5OukAmd2lvd8W8f11/47zz+dLMl37+uYrEcjiffzsYPy83n9e1tF4tJRrFyzolenuqUcn46yzcmlQL/nBecT8OKBxL/W1DFaPT0WrkWVHVHZZfLBRNA7g0AsmrOrgfD6/6hvL8u//7+mccP4/Px5O58/IuTRFbEEDUtFAclNSbXoyjyN/Wwt2AmLWaFpiqWwlBherHMoiLy+A+r23MgFipAnBtw4wiWs8DxxoDpqK43NRk/EuD3devrK+eSc7mG/bHeZ7eI9mbWyw7BxOItCJiVkTQphcPwGI/24Zk2r5TjTMrI/kLkWIjGK1A0eZ2kYARBNhgey0jRXDpaMdOxYrlhUrxS2oEpUFItiGBb8gYUTDUIVtQrRTgTmNIVAu6Vtt6mjHLFViTeWHcoGlO2gnffwjsyBs5scpvD3/D+ra+ZoVOSZDYJ54Rr+njSdXsbLHI3TpBVjeL7B6me0P3LxLZfac5JTDuIwU5C56TgpuNKRMeVkw4yJiMi6ayRuB3S0SxjnKiOFLSjCiEQwJ1f/3QVpwf1c6mWe5nPZq17J1Uu/kCr5b3wwhqAkK/AQN6SblqgWUoZ4WdIthgVgMykIka+VEiaiD3AlVH9Txt9hpKshYdwGOBSKdvwsMMAx7QuaKPEaOIKR9PRLgdFkc18kWySeU028BzoJlqzhaC0Bc/uUWW0q+kzRUSctgHfkcrIX7mIIIsWZFsKYDMuZy3Q8L2NHKqMdEr8d81M21Fq2ZZ0gFZ03pJsS9qjjWolJpxki9qD8bE1CI8WNO9IURk8zYlYFGTRBnuPQmTY9mBBti82S03yrmIBbDtBxWZFW+WzolnVvndB3WjD1RWsQru26PlOq5FLak2Wc0qWsZealSbcADpIltbgNq6PN7B/v9xnNZVuSWVUVX7/gQl7vN/r4Quz01zfKQwoLYTNjhSVP+XEpLYaYsOcKPr+BwigqJwR9Iq9Xq9MKXEzQEZVKzXAcwAkKk7R975fc7kjoS/LiHGTxtxKTjB5cEmSFny+RwHrX/L7dXtOPUZyjNgtiHWYOhdNNtUGTXYL8IpV30FIw+YsbjKINUnVA1z5QTMR0xPC+Qmi3LCYIe4xKth3jFaZtuEzz8Ej4QjET2WcIgVacJiinlRGfphOMCLJTUs1rQazZUBRDHbJlBiMYsP+4PK0f3k67E8GN6P+cDQ4v8eaIk8O1lyf9q/cmosRlp1d2jV5gWmtwmDJxWn/bNK/Hg3PRv1ruwSlOmQArnAk89SJyFNTmD1qgbnWaWX+c2U8evZkKRjHHKF8lH+v3X91/I1tCoD4VGY0R0fkT5M02+JqMDhoaWJZCLyE85Pumhj06mgXqlu7Ngj2fta1TKKnviB0R0YVdizHnVzJBxobXc3quFnVotrKNVuyA0vbse3N/OAbFJyhmDOlZDhVE6gW+2qME7JwKiBzKoKinfQh0o3FVGg8b2mnXzwAugqoD8eC47tJ54+wAu7Ik//C0ePdxEbe4Rne4ZlYAOsoAGvHlvHl5BN/uP/nYns/+bjt4gTDD+AjPEhNZXcEfTtvO9eHA4ipn22sWKKNOxbJqcrgbXsAZR8lHJB4v9vg33nSVlJ/jV0FNWtM/jtvYPd6NxicO3j8H5Hk0yGCFQAA", "encoding": "utf-8"}, "headers": {"X-XSS-Protection": ["1; mode=block"], "Content-Security-Policy": ["default-src 'none'"], "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"], "Transfer-Encoding": ["chunked"], "Last-Modified": ["Fri, 20 Jul 2018 15:19:36 GMT"], "Access-Control-Allow-Origin": ["*"], "X-Frame-Options": ["deny"], "Status": ["200 OK"], "X-GitHub-Request-Id": ["85B0:3FAD:10947D0:23340FB:5B625052"], "ETag": ["W/\"dd4ef8efe09f7951e6c100bf43e1cafd\""], "Date": ["Thu, 02 Aug 2018 00:29:06 GMT"], "X-RateLimit-Remaining": ["4999"], "Strict-Transport-Security": ["max-age=31536000; includeSubdomains; preload"], "Server": ["GitHub.com"], "X-OAuth-Scopes": ["read:user, repo:invite"], "X-GitHub-Media-Type": ["github.v3; param=full; format=json"], "X-Content-Type-Options": ["nosniff"], "Content-Encoding": ["gzip"], "X-Runtime-rack": ["0.060062"], "Vary": ["Accept, Authorization, Cookie, X-GitHub-OTP"], "X-RateLimit-Limit": ["5000"], "Cache-Control": ["private, max-age=60, s-maxage=60"], "Referrer-Policy": ["origin-when-cross-origin, strict-origin-when-cross-origin"], "Content-Type": ["application/json; charset=utf-8"], "X-Accepted-OAuth-Scopes": ["repo"], "X-RateLimit-Reset": ["1533173346"]}, "status": {"message": "OK", "code": 200}, "url": "https://api.github.com/repos/jacquerie/flask-shell-bpython"}, "recorded_at": "2018-08-02T00:29:06"}, {"request": {"body": {"string": "", "encoding": "utf-8"}, "headers": {"Accept-Encoding": ["gzip, deflate"], "Accept": ["application/vnd.github.v3.full+json"], "User-Agent": ["github3.py/1.1.0"], "Accept-Charset": ["utf-8"], "Connection": ["keep-alive"], "Content-Type": ["application/json"], "Authorization": ["token <AUTH_TOKEN>"]}, "method": "GET", "uri": "https://api.github.com/repos/jacquerie/flask-shell-bpython/invitations?per_page=100"}, "response": {"body": {"string": "", "base64_string": "H4sIAAAAAAAAA+2ZYW/bNhCG/4rgr3NMx3aa2EBQZOhWpICTrnC3osNgUBJtMaFIjaRsOEL++46kLMnG4sgxPwYo2kTVPTyRd6e7V38XHRp3Juf94ejy8qLf7XARk7m51Jl+ul3fsy8s+jx+wj++rSLOvoT8ZxZ//r2P/xrn09nN092naHg3u7nudDuSZEJRLeSmM3HQD+cXl4Px4HIX+tse9HEwnX0fTh8eN3frawPiOCWw/IJh9XimEsLYWZhtdCI4/OciZ2xe3vGAo39zIilB/3+vWHMijTNMLCkHZGUAIPOIw6vzwdXeM//x4c8fdyx6mI6ms9vR9Ma6hFdYYznPJQNKonWmJgi5i2rQW1Kd5GGuiIwE14TrXiRSlCOH/7i6HgFiKUuI3Vu4sAfLaMlxxgBTqOlvolO254Bb197fvHMhGBNrsN/39+ASqDIzu2wRlC/fggCzAgmdENgweIxn8/BU6SPdsSYFMv9AOBqIgiOQJD7OpdIIHDLB8FwgE6aWlocqkjTTVPAjXdsxBZSQS8zpE34DCkwVEGzuHPdg1gRMyQoC7khbZ1OgTNIVjjZmOySJCF3B7r6Ft2cMOL3JTA5/h/M3e001meM4NUm4wEyR527Hrq3hJnuhC1nVKr5fSPWYVIcJy34jGcMRCSAGg5gscM50YEtEYMtJABmTYh4Ha0jcAAeKppRhGQhOAplzDgEc/PrVVpweeL8Q8rFy82DW2jOpc/EFXw3vlQNrAYJ8BQy490g2HmiGUiD4u0y2CCoADoXEUNA94HdwBWr+aqJPE5x6WMViAJcI4WOHLQZwVKmctEqMNudmaQptc5DnaeiKZJvMa7OA44DfWCm65IR42NkKVaBtTQ8l5lHiA74lFcj9ZCMCLz24bSgAC5kIPdDgfYssqkAqwe69pud+PDVsQ9pBS7Lw5LYhVWgtvcSEddmgKjC8bDWEhweftyRUlDvNMF/meOmDXaEgMkx7sMRPrzZLbfKuZgHYdIKShrmv8lnTjNeud4G64WOra1iNtm3R4Yak1ZY0miy7KWlKX2tW2nBL0E6yeIObuN5fwPz+ep/V1nVDKlBd+d0Lplzj9F0v3zBbn5srlQOKh7DZklDxS4Z1YqohLJhhSU5/gBKEihBDr9jr9YqEYDsDpER6qQGOA0AsowT63tN9LrYk6MtSrO2ksTAuxzB5MIFjD3teoQDrDvl0vx2nGSMZjNgenLWYJheabKI0NNke4DWruQIXmi5o1GYQa5OqO7jio6I8Il3MWBeiXNOIQtzDqGDOGFpl4mPPHAceCSQQN5UxAingYcMkcaQCuWE6hhFJbDzVtAasA6Md5SsY+GC0q5WXlEYJTu3AavSPwdX44nI83BWGtsrL7egeBKH72fTa5OcB5WX4ovJS8k+QXhoOH55MGzceJbxs7d6uu+wRTpFdKtRJqktF8Se61MimXANxcbTmUpGOlVwqQ1syYOk2c5oZkhSqTP0ILg1PdsQa8Ol1vcUl5bscWsu373Louxxqvx74yc76a8Sb5FAiU2jFjBgNAqbTSqGjlgQ00niONVwc9M+vzvrw53w2GE4uxpOL0U9I/YOKpKlDVvJ2X2bmtgq4/ghtv/kA4/Ar7uB3FtRAdp7/+Q+M20i7UBoAAA==", "encoding": "utf-8"}, "headers": {"X-XSS-Protection": ["1; mode=block"], "Content-Security-Policy": ["default-src 'none'"], "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"], "Transfer-Encoding": ["chunked"], "Access-Control-Allow-Origin": ["*"], "X-Frame-Options": ["deny"], "Status": ["200 OK"], "X-GitHub-Request-Id": ["85B0:3FAD:10947E4:2334116:5B625052"], "ETag": ["W/\"02fdeb59420d3937cc69913557e3c135\""], "Date": ["Thu, 02 Aug 2018 00:29:06 GMT"], "X-RateLimit-Remaining": ["4998"], "Strict-Transport-Security": ["max-age=31536000; includeSubdomains; preload"], "Server": ["GitHub.com"], "X-OAuth-Scopes": ["read:user, repo:invite"], "X-GitHub-Media-Type": ["github.v3; param=full; format=json"], "X-Content-Type-Options": ["nosniff"], "Content-Encoding": ["gzip"], "X-Runtime-rack": ["0.060893"], "Vary": ["Accept, Authorization, Cookie, X-GitHub-OTP"], "X-RateLimit-Limit": ["5000"], "Cache-Control": ["private, max-age=60, s-maxage=60"], "Referrer-Policy": ["origin-when-cross-origin, strict-origin-when-cross-origin"], "Content-Type": ["application/json; charset=utf-8"], "X-Accepted-OAuth-Scopes": [""], "X-RateLimit-Reset": ["1533173346"]}, "status": {"message": "OK", "code": 200}, "url": "https://api.github.com/repos/jacquerie/flask-shell-bpython/invitations?per_page=100"}, "recorded_at": "2018-08-02T00:29:06"}, {"request": {"body": {"string": "", "encoding": "utf-8"}, "headers": {"Content-Length": ["0"], "Accept-Encoding": ["gzip, deflate"], "Accept": ["application/vnd.github.v3.full+json"], "User-Agent": ["github3.py/1.1.0"], "Accept-Charset": ["utf-8"], "Connection": ["keep-alive"], "Content-Type": ["application/json"], "Authorization": ["token <AUTH_TOKEN>"]}, "method": "DELETE", "uri": "https://api.github.com/repos/jacquerie/flask-shell-bpython/invitations/10347750"}, "response": {"body": {"string": "", "encoding": null}, "headers": {"Status": ["204 No Content"], "X-RateLimit-Remaining": ["4997"], "X-GitHub-Media-Type": ["github.v3; param=full; format=json"], "X-Runtime-rack": ["0.067571"], "X-Content-Type-Options": ["nosniff"], "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"], "X-GitHub-Request-Id": ["85B0:3FAD:10947F2:2334137:5B625052"], "Strict-Transport-Security": ["max-age=31536000; includeSubdomains; preload"], "X-XSS-Protection": ["1; mode=block"], "Server": ["GitHub.com"], "Content-Security-Policy": ["default-src 'none'"], "X-RateLimit-Limit": ["5000"], "Date": ["Thu, 02 Aug 2018 00:29:06 GMT"], "X-OAuth-Scopes": ["read:user, repo:invite"], "Referrer-Policy": ["origin-when-cross-origin, strict-origin-when-cross-origin"], "Content-Type": ["application/octet-stream"], "X-Accepted-OAuth-Scopes": [""], "X-Frame-Options": ["deny"], "X-RateLimit-Reset": ["1533173346"]}, "status": {"message": "No Content", "code": 204}, "url": "https://api.github.com/repos/jacquerie/flask-shell-bpython/invitations/10347750"}, "recorded_at": "2018-08-02T00:29:07"}], "recorded_with": "betamax/0.8.1"}

0 commit comments

Comments
 (0)
0