diff --git a/doc/apis.rst b/doc/apis.rst index 48e4601c..738edb0a 100644 --- a/doc/apis.rst +++ b/doc/apis.rst @@ -104,6 +104,14 @@ APIs * GET: :meth:`github.Repository.Repository.get_network_events` +* ``/notifications`` + + * GET: :meth:`github.AuthenticatedUser.AuthenticatedUser.get_notifications` + +* ``/notifications/threads/:id`` + + * GET: :meth:`github.AuthenticatedUser.AuthenticatedUser.get_notification` + * ``/orgs/:org`` * GET: :meth:`github.MainClass.Github.get_organization` diff --git a/doc/conf.py b/doc/conf.py index 9e311aa9..958de460 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -252,7 +252,7 @@ with open("github_objects.rst", "w") as github_objects: github_objects.write("\n") github_objects.write(".. toctree::\n") - for obj in ["AuthenticatedUser", "Authorization", "AuthorizationApplication", "Branch", "Commit", "CommitComment", "CommitStats", "CommitStatus", "Comparison", "ContentFile", "Download", "Event", "File", "Gist", "GistComment", "GistFile", "GistHistoryState", "GitAuthor", "GitBlob", "GitCommit", "GitObject", "GitignoreTemplate", "GitRef", "GitTag", "GitTree", "GitTreeElement", "Hook", "HookDescription", "HookResponse", "Issue", "IssueComment", "IssueEvent", "IssuePullRequest", "Label", "Milestone", "NamedUser", "Organization", "Permissions", "Plan", "PullRequest", "PullRequestComment", "PullRequestMergeStatus", "PullRequestPart", "Repository", "RepositoryKey", "Tag", "Team", "UserKey"]: + for obj in ["AuthenticatedUser", "Authorization", "AuthorizationApplication", "Branch", "Commit", "CommitComment", "CommitStats", "CommitStatus", "Comparison", "ContentFile", "Download", "Event", "File", "Gist", "GistComment", "GistFile", "GistHistoryState", "GitAuthor", "GitBlob", "GitCommit", "GitObject", "GitignoreTemplate", "GitRef", "GitTag", "GitTree", "GitTreeElement", "Hook", "HookDescription", "HookResponse", "Issue", "IssueComment", "IssueEvent", "IssuePullRequest", "Label", "Milestone", "NamedUser", "Notification", "NotificationSubject", "Organization", "Permissions", "Plan", "PullRequest", "PullRequestComment", "PullRequestMergeStatus", "PullRequestPart", "Repository", "RepositoryKey", "Tag", "Team", "UserKey"]: github_objects.write(" github_objects/" + obj + "\n") with open("github_objects/" + obj + ".rst", "w") as github_object: github_object.write(obj + "\n") diff --git a/github/AuthenticatedUser.py b/github/AuthenticatedUser.py index 9e01a059..1166b83f 100644 --- a/github/AuthenticatedUser.py +++ b/github/AuthenticatedUser.py @@ -617,6 +617,44 @@ class AuthenticatedUser(github.GithubObject.CompletableGithubObject): None ) + def get_notification(self, id): + """ + :calls: `GET /notifications/threads/:id `_ + :rtype: :class:`github.Notification.Notification` + """ + + assert isinstance(id, (str, unicode)), id + headers, data = self._requester.requestJsonAndCheck( + "GET", + "/notifications/threads/" + id, + None, + None + ) + return github.Notification.Notification(self._requester, data, completed=True) + + def get_notifications(self, all=github.GithubObject.NotSet, participating=github.GithubObject.NotSet): + """ + :calls: `GET /notifications `_ + :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.Notification.Notification` + """ + + assert all is github.GithubObject.NotSet or isinstance(all, bool), all + assert participating is github.GithubObject.NotSet or isinstance(participating, bool), participating + + params = dict() + if all is not github.GithubObject.NotSet: + params["all"] = all + if participating is not github.GithubObject.NotSet: + params["participating"] = participating + # TODO: implement parameter "since" + + return github.PaginatedList.PaginatedList( + github.Notification.Notification, + self._requester, + "/notifications", + params + ) + def get_organization_events(self, org): """ :calls: `GET /users/:user/events/orgs/:org `_ diff --git a/github/MainClass.py b/github/MainClass.py index d8a04711..c12ff2ae 100644 --- a/github/MainClass.py +++ b/github/MainClass.py @@ -26,6 +26,7 @@ import Legacy import github.GithubObject import HookDescription import GitignoreTemplate +import Notification DEFAULT_BASE_URL = "https://api.github.com" diff --git a/github/Notification.py b/github/Notification.py new file mode 100644 index 00000000..3c1b3c57 --- /dev/null +++ b/github/Notification.py @@ -0,0 +1,113 @@ +# -*- coding: utf-8 -*- + +# Copyright 2013 Peter Golm and Vincent Jacques +# golm.peter@gmail.com +# vincent@vincent-jacques.net + +# This file is part of PyGithub. http://jacquev6.github.com/PyGithub/ + +# 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 github.GithubObject + +import github.Repository +import github.NotificationSubject + + +class Notification(github.GithubObject.CompletableGithubObject): + """ + This class represents Notifications as returned for example by http://developer.github.com/v3/activity/notifications/#list-your-notifications + """ + + @property + def id(self): + """ + :type: string + """ + self._completeIfNotSet(self._id) + return self._NoneIfNotSet(self._id) + + @property + def repository(self): + """ + :type: :class:`github.Repository.Repository` + """ + self._completeIfNotSet(self._repository) + return self._NoneIfNotSet(self._repository) + + @property + def subject(self): + """ + :type: :class:`github.NotificationSubject.NotificationSubject` + """ + self._completeIfNotSet(self._subject) + return self._NoneIfNotSet(self._subject) + + @property + def reason(self): + """ + :type: string + """ + self._completeIfNotSet(self._reason) + return self._NoneIfNotSet(self._reason) + + @property + def unread(self): + """ + :type: bool + """ + self._completeIfNotSet(self._unread) + return self._NoneIfNotSet(self._unread) + + @property + def updated_at(self): + """ + :type: datetime.datetime + """ + self._completeIfNotSet(self._updated_at) + return self._NoneIfNotSet(self._updated_at) + + @property + def url(self): + """ + :type: string + """ + self._completeIfNotSet(self._url) + return self._NoneIfNotSet(self._url) + + def _initAttributes(self): + self._id = github.GithubObject.NotSet + self._repository = github.GithubObject.NotSet + self._reason = github.GithubObject.NotSet + self._unread = github.GithubObject.NotSet + self._updated_at = github.GithubObject.NotSet + self._url = github.GithubObject.NotSet + + def _useAttributes(self, attributes): + if "id" in attributes: # pragma no branch + assert attributes["id"] is None or isinstance(attributes["id"], (str, unicode)), attributes["id"] + self._id = attributes["id"] + if "repository" in attributes: # pragma no branch + assert attributes["repository"] is None or isinstance(attributes["repository"], dict), attributes["repository"] + self._repository = None if attributes["repository"] is None else github.Repository.Repository(self._requester, attributes["repository"], completed=False) + if "subject" in attributes: # pragma no branch + assert attributes["subject"] is None or isinstance(attributes["subject"], dict), attributes["subject"] + self._subject = None if attributes["subject"] is None else github.NotificationSubject.NotificationSubject(self._requester, attributes["subject"], completed=False) + if "reason" in attributes: # pragma no branch + assert attributes["reason"] is None or isinstance(attributes["reason"], (str, unicode)), attributes["reason"] + self._reason = attributes["reason"] + if "unread" in attributes: # pragma no branch + assert attributes["unread"] is None or isinstance(attributes["unread"], bool), attributes["unread"] + self._unread = attributes["unread"] + if "updated_at" in attributes: # pragma no branch + assert attributes["updated_at"] is None or isinstance(attributes["updated_at"], (str, unicode)), attributes["updated_at"] + self._updated_at = self._parseDatetime(attributes["updated_at"]); + if "url" in attributes: # pragma no branch + assert attributes["url"] is None or isinstance(attributes["url"], (str, unicode)), attributes["url"] + self._url = attributes["url"] diff --git a/github/NotificationSubject.py b/github/NotificationSubject.py new file mode 100644 index 00000000..f74a9c35 --- /dev/null +++ b/github/NotificationSubject.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- + +# Copyright 2013 Peter Golm and Vincent Jacques +# golm.peter@gmail.com +# vincent@vincent-jacques.net + +# This file is part of PyGithub. http://jacquev6.github.com/PyGithub/ + +# 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 github.GithubObject + + +class NotificationSubject(github.GithubObject.NonCompletableGithubObject): + """ + This class represents Subjects of Notifications as returned for example by http://developer.github.com/v3/activity/notifications/#list-your-notifications + """ + + @property + def title(self): + """ + :type: string + """ + return self._NoneIfNotSet(self._title) + + @property + def url(self): + """ + :type: string + """ + return self._NoneIfNotSet(self._url) + + @property + def latest_comment_url(self): + """ + :type: string + """ + return self._NoneIfNotSet(self._latest_comment_url) + + @property + def type(self): + """ + :type: string + """ + return self._NoneIfNotSet(self._type) + + def _initAttributes(self): + self._title = github.GithubObject.NotSet + self._url = github.GithubObject.NotSet + self._latest_comment_url = github.GithubObject.NotSet + self._type = github.GithubObject.NotSet + + def _useAttributes(self, attributes): + if "title" in attributes: # pragma no branch + assert attributes["title"] is None or isinstance(attributes["title"], (str, unicode)), attributes["title"] + self._title = attributes["title"] + if "url" in attributes: # pragma no branch + assert attributes["url"] is None or isinstance(attributes["url"], (str, unicode)), attributes["url"] + self._url = attributes["url"] + if "latest_comment_url" in attributes: # pragma no branch + assert attributes["latest_comment_url"] is None or isinstance(attributes["latest_comment_url"], (str, unicode)), attributes["latest_comment_url"] + self._latest_comment_url = attributes["latest_comment_url"] + if "type" in attributes: # pragma no branch + assert attributes["type"] is None or isinstance(attributes["type"], (str, unicode)), attributes["type"] + self._type = attributes["type"] diff --git a/github/tests/AuthenticatedUser.py b/github/tests/AuthenticatedUser.py index 8ae20d7a..42ef98fe 100644 --- a/github/tests/AuthenticatedUser.py +++ b/github/tests/AuthenticatedUser.py @@ -185,3 +185,22 @@ class AuthenticatedUser(Framework.TestCase): def testCreateFork(self): repo = self.user.create_fork(self.g.get_user("nvie").get_repo("gitflow")) self.assertEqual(repo.source.full_name, "nvie/gitflow") + + def testGetNotification(self): + notification = self.user.get_notification("8406712") + self.assertEqual(notification.id, "8406712") + self.assertEqual(notification.unread, False) + self.assertEqual(notification.reason, "author") + self.assertEqual(notification.subject.title, "Feature/coveralls") + self.assertEqual(notification.subject.type, "PullRequest") + self.assertEqual(notification.repository.id, 8432784) + self.assertEqual(notification.updated_at, datetime.datetime(2013, 3, 15, 5, 43, 11)) + self.assertEqual(notification.url, None) + self.assertEqual(notification.subject.url, None) + self.assertEqual(notification.subject.latest_comment_url, None) + + def testGetNotifications(self): + self.assertListKeyEqual(self.user.get_notifications(participating=True), lambda n: n.id, ["8406712"]) + + def testGetNotificationsWithOtherArguments(self): + self.assertListKeyEqual(self.user.get_notifications(all=True), lambda n: n.id, []) diff --git a/github/tests/ReplayData/AuthenticatedUser.testGetNotification.txt b/github/tests/ReplayData/AuthenticatedUser.testGetNotification.txt new file mode 100644 index 00000000..06739072 --- /dev/null +++ b/github/tests/ReplayData/AuthenticatedUser.testGetNotification.txt @@ -0,0 +1,4 @@ +https GET api.github.com None /notifications/threads/8406712 {'Authorization': 'Basic login_and_password_removed'} null +200 +[('status', '200 OK'), ('x-ratelimit-remaining', '4999'), ('x-github-media-type', 'github.beta; format=json'), ('x-content-type-options', 'nosniff'), ('x-ratelimit-limit', '5000'), ('vary', 'Accept, Authorization, Cookie'), ('content-length', '16567'), ('server', 'nginx'), ('last-modified', 'Fri, 24 Aug 2012 07:05:12 GMT'), ('connection', 'keep-alive'), ('etag', '"eb52c03081d2fc22f26ed2718921e500"'), ('cache-control', 'private, max-age=60, s-maxage=60'), ('date', 'Sat, 08 Sep 2012 17:26:28 GMT'), ('content-type', 'application/json; charset=utf-8')] +{"id": "8406712","unread": false,"reason": "author","updated_at": "2013-03-15T05:43:11Z","last_read_at": "2013-03-15T06:06:34Z","subject": {"title": "Feature/coveralls","type": "PullRequest"},"repository": {"id": 8432784}} diff --git a/github/tests/ReplayData/AuthenticatedUser.testGetNotifications.txt b/github/tests/ReplayData/AuthenticatedUser.testGetNotifications.txt new file mode 100644 index 00000000..150f872a --- /dev/null +++ b/github/tests/ReplayData/AuthenticatedUser.testGetNotifications.txt @@ -0,0 +1,4 @@ +https GET api.github.com None /notifications?participating=True {'Authorization': 'Basic login_and_password_removed'} null +200 +[('status', '200 OK'), ('x-ratelimit-remaining', '4999'), ('x-github-media-type', 'github.beta; format=json'), ('x-content-type-options', 'nosniff'), ('x-ratelimit-limit', '5000'), ('vary', 'Accept, Authorization, Cookie'), ('content-length', '16567'), ('server', 'nginx'), ('last-modified', 'Fri, 24 Aug 2012 07:05:12 GMT'), ('connection', 'keep-alive'), ('etag', '"eb52c03081d2fc22f26ed2718921e500"'), ('cache-control', 'private, max-age=60, s-maxage=60'), ('date', 'Sat, 08 Sep 2012 17:26:28 GMT'), ('content-type', 'application/json; charset=utf-8')] +[{"id": "8406712","unread": false,"reason": "author","updated_at": "2013-03-15T05:43:11Z","last_read_at": "2013-03-15T06:06:34Z","subject": {"title": "Feature/coveralls","type": "PullRequest"},"repository": {"id": 8432784}}] diff --git a/github/tests/ReplayData/AuthenticatedUser.testGetNotificationsWithOtherArguments.txt b/github/tests/ReplayData/AuthenticatedUser.testGetNotificationsWithOtherArguments.txt new file mode 100644 index 00000000..88a78000 --- /dev/null +++ b/github/tests/ReplayData/AuthenticatedUser.testGetNotificationsWithOtherArguments.txt @@ -0,0 +1,5 @@ +https GET api.github.com None /notifications?all=True {'Authorization': 'Basic login_and_password_removed'} null +200 +[('status', '200 OK'), ('x-ratelimit-remaining', '4999'), ('x-github-media-type', 'github.beta; format=json'), ('x-content-type-options', 'nosniff'), ('content-length', '2'), ('server', 'GitHub.com'), ('connection', 'keep-alive'), ('x-ratelimit-limit', '5000'), ('etag', '"d751713988987e9331980363e24189ce"'), ('cache-control', 'max-age=0, private, must-revalidate'), ('date', 'Tue, 19 Mar 2013 21:05:52 GMT'), ('content-type', 'application/json; charset=utf-8')] +[] +