diff --git a/github/GithubObject.pyi b/github/GithubObject.pyi index 38f2c7e11e..be39afdbd7 100644 --- a/github/GithubObject.pyi +++ b/github/GithubObject.pyi @@ -39,6 +39,10 @@ class GithubObject: def _makeIntAttribute( value: Optional[Union[int, str]] ) -> Union[_ValuedAttribute, _BadAttribute]: ... + @staticmethod + def _makeFloatAttribute( + value: Optional[Union[float, str]] + ) -> Union[_ValuedAttribute, _BadAttribute]: ... def _makeListOfClassesAttribute( self, klass: Any, value: Any ) -> Union[_ValuedAttribute, _BadAttribute]: ... @@ -119,6 +123,10 @@ class _BadAttribute: class _NotSetType: def __repr__(self) -> str: ... + value = None class _ValuedAttribute: def __init__(self, value: Any) -> None: ... + value: Any + +NotSet: _NotSetType diff --git a/github/HookDelivery.py b/github/HookDelivery.py new file mode 100644 index 0000000000..c7f85051b7 --- /dev/null +++ b/github/HookDelivery.py @@ -0,0 +1,300 @@ +############################ Copyrights and license ############################ +# # +# Copyright 2012 Vincent Jacques # +# Copyright 2012 Zearin # +# Copyright 2013 AKFish # +# Copyright 2013 Vincent Jacques # +# Copyright 2014 Vincent Jacques # +# Copyright 2016 Jannis Gebauer # +# Copyright 2016 Peter Buckley # +# Copyright 2018 Wan Liuyang # +# 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 . # +# # +################################################################################ +from typing import Any, Dict, Optional, Union + +import github.GithubObject +from github.GithubObject import _BadAttribute, _NotSetType, _ValuedAttribute + + +class HookDeliverySummary(github.GithubObject.NonCompletableGithubObject): + """ + This class represents a Summary of HookDeliveries + """ + + def __repr__(self) -> str: + return self.get__repr__({"id": self._id.value}) + + @property + def id(self) -> Optional[int]: + """ + :type: integer + """ + return self._id.value + + @property + def guid(self) -> Optional[str]: + """ + :type: string + """ + return self._guid.value + + @property + def delivered_at(self) -> Optional[str]: + """ + :type: string + """ + return self._delivered_at.value + + @property + def redelivery(self) -> Optional[bool]: + """ + :type: boolean + """ + return self._redelivery.value + + @property + def duration(self) -> Optional[float]: + """ + :type: float + """ + return self._duration.value + + @property + def status(self) -> Optional[str]: + """ + :type: string + """ + return self._status.value + + @property + def status_code(self) -> Optional[int]: + """ + :type: integer + """ + return self._status_code.value + + @property + def event(self) -> Optional[str]: + """ + :type: string + """ + return self._event.value + + @property + def action(self) -> Optional[str]: + """ + :type: string + """ + return self._action.value + + @property + def installation_id(self) -> Optional[int]: + """ + :type: integer + """ + return self._installation_id.value + + @property + def repository_id(self) -> Optional[int]: + """ + :type: integer + """ + return self._repository_id.value + + @property + def url(self) -> Optional[str]: + """ + :type: string + """ + return self._url.value + + def _initAttributes(self) -> None: + self._id: Union[ + _ValuedAttribute, _BadAttribute, _NotSetType + ] = github.GithubObject.NotSet + self._guid: Union[ + _ValuedAttribute, _BadAttribute, _NotSetType + ] = github.GithubObject.NotSet + self._delivered_at: Union[ + _ValuedAttribute, _BadAttribute, _NotSetType + ] = github.GithubObject.NotSet + self._redelivery: Union[ + _ValuedAttribute, _BadAttribute, _NotSetType + ] = github.GithubObject.NotSet + self._duration: Union[ + _ValuedAttribute, _BadAttribute, _NotSetType + ] = github.GithubObject.NotSet + self._status: Union[ + _ValuedAttribute, _BadAttribute, _NotSetType + ] = github.GithubObject.NotSet + self._status_code: Union[ + _ValuedAttribute, _BadAttribute, _NotSetType + ] = github.GithubObject.NotSet + self._event: Union[ + _ValuedAttribute, _BadAttribute, _NotSetType + ] = github.GithubObject.NotSet + self._action: Union[ + _ValuedAttribute, _BadAttribute, _NotSetType + ] = github.GithubObject.NotSet + self._installation_id: Union[ + _ValuedAttribute, _BadAttribute, _NotSetType + ] = github.GithubObject.NotSet + self._repository_id: Union[ + _ValuedAttribute, _BadAttribute, _NotSetType + ] = github.GithubObject.NotSet + self._url: Union[ + _ValuedAttribute, _BadAttribute, _NotSetType + ] = github.GithubObject.NotSet + + def _useAttributes(self, attributes: Dict[str, Any]): + if "id" in attributes: # pragma no branch + self._id = self._makeIntAttribute(attributes["id"]) + if "guid" in attributes: # pragma no branch + self._guid = self._makeStringAttribute(attributes["guid"]) + if "delivered_at" in attributes: # pragma no branch + self._delivered_at = self._makeStringAttribute(attributes["delivered_at"]) + if "redelivery" in attributes: # pragma no branch + self._redelivery = self._makeBoolAttribute(attributes["redelivery"]) + if "duration" in attributes: # pragma no branch + self._duration = self._makeFloatAttribute(attributes["duration"]) + if "status" in attributes: # pragma no branch + self._status = self._makeStringAttribute(attributes["status"]) + if "status_code" in attributes: # pragma no branch + self._status_code = self._makeIntAttribute(attributes["status_code"]) + if "event" in attributes: # pragma no branch + self._event = self._makeStringAttribute(attributes["event"]) + if "action" in attributes: # pragma no branch + self._action = self._makeStringAttribute(attributes["action"]) + if "installation_id" in attributes: # pragma no branch + self._installation_id = self._makeIntAttribute( + attributes["installation_id"] + ) + if "repository_id" in attributes: # pragma no branch + self._repository_id = self._makeIntAttribute(attributes["repository_id"]) + if "url" in attributes: # pragma no branch + self._url = self._makeStringAttribute(attributes["url"]) + + +class HookDeliveryRequest(github.GithubObject.NonCompletableGithubObject): + """ + This class represents a HookDeliveryRequest + """ + + @property + def headers(self) -> Optional[dict]: + """ + :type: dict + """ + return self._headers.value + + @property + def payload(self) -> Optional[dict]: + """ + :type: dict + """ + return self._payload.value + + def _initAttributes(self) -> None: + self._headers: Union[ + _ValuedAttribute, _BadAttribute, _NotSetType + ] = github.GithubObject.NotSet + self._payload: Union[ + _ValuedAttribute, _BadAttribute, _NotSetType + ] = github.GithubObject.NotSet + + def _useAttributes(self, attributes: Dict[str, Any]) -> None: + if "headers" in attributes: # pragma no branch + self._headers = self._makeDictAttribute(attributes["headers"]) + if "payload" in attributes: # pragma no branch + self._payload = self._makeDictAttribute(attributes["payload"]) + + +class HookDeliveryResponse(github.GithubObject.NonCompletableGithubObject): + """ + This class represents a HookDeliveryResponse + """ + + @property + def headers(self) -> Optional[dict]: + """ + :type: dict + """ + return self._headers.value + + @property + def payload(self) -> Optional[dict]: + """ + :type: string + """ + return self._payload.value + + def _initAttributes(self) -> None: + self._headers: Union[ + _ValuedAttribute, _BadAttribute, _NotSetType + ] = github.GithubObject.NotSet + self._payload: Union[ + _ValuedAttribute, _BadAttribute, _NotSetType + ] = github.GithubObject.NotSet + + def _useAttributes(self, attributes: Dict[str, Any]) -> None: + if "headers" in attributes: # pragma no branch + self._headers = self._makeDictAttribute(attributes["headers"]) + if "payload" in attributes: # pragma no branch + self._payload = self._makeDictAttribute(attributes["payload"]) + + +class HookDelivery(HookDeliverySummary): + """ + This class represents a HookDelivery + """ + + @property + def request(self) -> Optional[HookDeliveryRequest]: + """ + :type: HookDeliveryRequest + """ + return self._request.value + + @property + def response(self) -> Optional[HookDeliveryResponse]: + """ + :type: HookDeliveryResponse + """ + return self._response.value + + def _initAttributes(self) -> None: + super()._initAttributes() + self._request: Union[ + _ValuedAttribute, _BadAttribute, _NotSetType + ] = github.GithubObject.NotSet + self._response: Union[ + _ValuedAttribute, _BadAttribute, _NotSetType + ] = github.GithubObject.NotSet + + def _useAttributes(self, attributes: Dict[str, Any]) -> None: + super()._useAttributes(attributes) + if "request" in attributes: # pragma no branch + self._request = self._makeClassAttribute( + HookDeliveryRequest, attributes["request"] + ) + if "response" in attributes: # pragma no branch + self._response = self._makeClassAttribute( + HookDeliveryResponse, attributes["response"] + ) diff --git a/github/MainClass.py b/github/MainClass.py index 5b438d6ce4..c1a417aaeb 100644 --- a/github/MainClass.py +++ b/github/MainClass.py @@ -49,6 +49,7 @@ import datetime import pickle +from typing import List import urllib3 @@ -66,10 +67,12 @@ Consts, GithubApp, GitignoreTemplate, + HookDelivery, HookDescription, RateLimit, Repository, ) +from .HookDelivery import HookDeliverySummary from .Requester import Requester @@ -689,6 +692,39 @@ def get_hooks(self): for attributes in data ] + def get_hook_delivery(self, hook_id: int, delivery_id: int) -> HookDelivery: + """ + :calls: `GET /hooks/{hook_id}/deliveries/{delivery_id} `_ + :param hook_id: integer + :param delivery_id: integer + :rtype: :class:`github.HookDelivery.HookDelivery` + """ + assert isinstance(hook_id, int), hook_id + assert isinstance(delivery_id, int), delivery_id + headers, attributes = self.__requester.requestJsonAndCheck( + "GET", f"/hooks/{hook_id}/deliveries/{delivery_id}" + ) + return HookDelivery.HookDelivery( + self.__requester, headers, attributes, completed=True + ) + + def get_hook_deliveries(self, hook_id: int) -> List[HookDeliverySummary]: + """ + :calls: `GET /hooks/{hook_id}/deliveries `_ + :param hook_id: integer + :rtype: list of :class:`github.HookDelivery.HookDeliverySummary` + """ + assert isinstance(hook_id, int), hook_id + headers, data = self.__requester.requestJsonAndCheck( + "GET", f"/hooks/{hook_id}/deliveries" + ) + return [ + HookDelivery.HookDeliverySummary( + self.__requester, headers, attributes, completed=True + ) + for attributes in data + ] + def get_gitignore_templates(self): """ :calls: `GET /gitignore/templates `_ diff --git a/github/MainClass.pyi b/github/MainClass.pyi index 63ccf786a9..b98e9ac069 100644 --- a/github/MainClass.pyi +++ b/github/MainClass.pyi @@ -2,6 +2,8 @@ from datetime import datetime from io import BytesIO from typing import Any, Dict, List, Optional, Tuple, Type, TypeVar, Union, overload +from urllib3.util import Retry + from github.AppAuthentication import AppAuthentication from github.AuthenticatedUser import AuthenticatedUser from github.Commit import Commit @@ -21,8 +23,6 @@ from github.RateLimit import RateLimit from github.Repository import Repository from github.Topic import Topic -from urllib3.util import Retry - TGithubObject = TypeVar("TGithubObject", bound=GithubObject) class Github: diff --git a/github/Organization.py b/github/Organization.py index a63cc8e3c5..239d76615c 100644 --- a/github/Organization.py +++ b/github/Organization.py @@ -876,6 +876,40 @@ def get_hooks(self): github.Hook.Hook, self._requester, f"{self.url}/hooks", None ) + def get_hook_delivery( + self, hook_id: int, delivery_id: int + ) -> github.HookDelivery.HookDelivery: + """ + :calls: `GET /orgs/{owner}/hooks/{hook_id}/deliveries/{delivery_id} `_ + :param hook_id: integer + :param delivery_id: integer + :rtype: :class:`github.HookDelivery.HookDelivery` + """ + assert isinstance(hook_id, int), hook_id + assert isinstance(delivery_id, int), delivery_id + headers, data = self._requester.requestJsonAndCheck( + "GET", f"{self.url}/hooks/{hook_id}/deliveries/{delivery_id}" + ) + return github.HookDelivery.HookDelivery( + self._requester, headers, data, completed=True + ) + + def get_hook_deliveries( + self, hook_id: int + ) -> github.PaginatedList.PaginatedList[github.HookDelivery.HookDeliverySummary]: + """ + :calls: `GET /orgs/{owner}/hooks/{hook_id}/deliveries `_ + :param hook_id: integer + :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.HookDelivery.HookDeliverySummary` + """ + assert isinstance(hook_id, int), hook_id + return github.PaginatedList.PaginatedList( + github.HookDelivery.HookDeliverySummary, + self._requester, + f"{self.url}/hooks/{hook_id}/deliveries", + None, + ) + def get_issues( self, filter=github.GithubObject.NotSet, diff --git a/github/Organization.pyi b/github/Organization.pyi index 7dfbbb1e23..aefaaae746 100644 --- a/github/Organization.pyi +++ b/github/Organization.pyi @@ -4,10 +4,10 @@ from typing import Any, Dict, List, Optional, Union from github.Event import Event from github.GithubObject import CompletableGithubObject, _NotSetType from github.Hook import Hook +from github.Installation import Installation from github.Issue import Issue from github.Label import Label from github.Migration import Migration -from github.Installation import Installation from github.NamedUser import NamedUser from github.PaginatedList import PaginatedList from github.Plan import Plan diff --git a/github/PaginatedList.py b/github/PaginatedList.py index f3524e4ec2..244041f324 100644 --- a/github/PaginatedList.py +++ b/github/PaginatedList.py @@ -34,9 +34,11 @@ # along with PyGithub. If not, see . # # # ################################################################################ - +from typing import Generic, TypeVar from urllib.parse import parse_qs +T = TypeVar("T") + class PaginatedListBase: def __init__(self): @@ -88,7 +90,7 @@ def __finished(self, index): return self.__stop is not None and index >= self.__stop -class PaginatedList(PaginatedListBase): +class PaginatedList(PaginatedListBase, Generic[T]): """ This class abstracts the `pagination of the API `_. diff --git a/github/Repository.py b/github/Repository.py index f36fe1df72..332fa35b1d 100644 --- a/github/Repository.py +++ b/github/Repository.py @@ -117,6 +117,7 @@ import github.GitTag import github.GitTree import github.Hook +import github.HookDelivery import github.Invitation import github.Issue import github.IssueEvent @@ -2534,6 +2535,41 @@ def get_hooks(self): github.Hook.Hook, self._requester, f"{self.url}/hooks", None ) + def get_hook_delivery( + self, hook_id: int, delivery_id: int + ) -> github.HookDelivery.HookDelivery: + """ + :calls: `GET /repos/{owner}/{repo}/hooks/{hook_id}/deliveries/{delivery_id} `_ + :param hook_id: integer + :param delivery_id: integer + :rtype: :class:`github.HookDelivery.HookDelivery` + """ + assert isinstance(hook_id, int), hook_id + assert isinstance(delivery_id, int), delivery_id + headers, data = self._requester.requestJsonAndCheck( + "GET", f"{self.url}/hooks/{hook_id}/deliveries/{delivery_id}" + ) + return github.HookDelivery.HookDelivery( + self._requester, headers, data, completed=True + ) + + def get_hook_deliveries( + self, hook_id: int + ) -> github.PaginatedList.PaginatedList[github.HookDelivery.HookDeliverySummary]: + """ + :calls: `GET /repos/{owner}/{repo}/hooks/{hook_id}/deliveries `_ + :param hook_id: integer + :rtype: :class:`github.PaginatedList.PaginatedList` of :class:`github.HookDelivery.HookDeliverySummary` + """ + assert isinstance(hook_id, int), hook_id + + return github.PaginatedList.PaginatedList( + github.HookDelivery.HookDeliverySummary, + self._requester, + f"{self.url}/hooks/{hook_id}/deliveries", + None, + ) + def get_issue(self, number): """ :calls: `GET /repos/{owner}/{repo}/issues/{number} `_ diff --git a/tests/Organization.py b/tests/Organization.py index 25b5336d9f..327eeb13a8 100644 --- a/tests/Organization.py +++ b/tests/Organization.py @@ -185,6 +185,15 @@ def testGetHook(self): def testGetHooks(self): self.assertListKeyEqual(self.org.get_hooks(), lambda h: h.id, [257993]) + def testGetHookDelivery(self): + delivery = self.org.get_hook_delivery(257993, 12345) + self.assertEqual(delivery.id, 12345) + + def testGetHookDeliveries(self): + self.assertListKeyEqual( + self.org.get_hook_deliveries(257993), lambda d: d.id, [12345] + ) + def testGetIssues(self): self.assertListKeyEqual(self.org.get_issues(), lambda i: i.id, []) diff --git a/tests/ReplayData/Organization.testGetHookDeliveries.txt b/tests/ReplayData/Organization.testGetHookDeliveries.txt new file mode 100644 index 0000000000..bdc53e639a --- /dev/null +++ b/tests/ReplayData/Organization.testGetHookDeliveries.txt @@ -0,0 +1,10 @@ +https +GET +api.github.com +None +/orgs/BeaverSoftware/hooks/257993/deliveries +{'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python'} +None +200 +[('status', '200 OK'), ('x-ratelimit-remaining', '4953'), ('content-length', '295'), ('server', 'nginx/1.0.13'), ('connection', 'keep-alive'), ('x-ratelimit-limit', '5000'), ('etag', '"07e5e1a2fafd1a5e2de62eb3afd007d5"'), ('date', 'Sun, 27 May 2012 07:02:25 GMT'), ('content-type', 'application/json; charset=utf-8')] +[{"id":12345,"guid":"abcde-12345","delivered_at":"2012-05-27T06:00:32Z","redelivery":false,"duration":0.27,"status":"OK","status_code":200,"event":"issues","action":"opened","installation_id":123,"repository_id":456,"url":"https://www.example-webhook.com"}] diff --git a/tests/ReplayData/Organization.testGetHookDelivery.txt b/tests/ReplayData/Organization.testGetHookDelivery.txt new file mode 100644 index 0000000000..53fb26d9c8 --- /dev/null +++ b/tests/ReplayData/Organization.testGetHookDelivery.txt @@ -0,0 +1,10 @@ +https +GET +api.github.com +None +/orgs/BeaverSoftware/hooks/257993/deliveries/12345 +{'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python'} +None +200 +[('status', '200 OK'), ('x-ratelimit-remaining', '4953'), ('content-length', '295'), ('server', 'nginx/1.0.13'), ('connection', 'keep-alive'), ('x-ratelimit-limit', '5000'), ('etag', '"07e5e1a2fafd1a5e2de62eb3afd007d5"'), ('date', 'Sun, 27 May 2012 07:02:25 GMT'), ('content-type', 'application/json; charset=utf-8')] +{"id":12345,"guid":"abcde-12345","delivered_at":"2012-05-27T06:00:32Z","redelivery":false,"duration":0.27,"status":"OK","status_code":200,"event":"issues","action":"opened","installation_id":123,"repository_id":456,"url":"https://www.example-webhook.com","request":{"headers":{"content-type": "application/json"},"payload":{"action": "opened"}},"response":{"headers":{"content-type": "text/html;charset=utf-8"},"payload":"ok"}} diff --git a/tests/ReplayData/Repository.testGetHookDeliveries.txt b/tests/ReplayData/Repository.testGetHookDeliveries.txt new file mode 100644 index 0000000000..b54039833d --- /dev/null +++ b/tests/ReplayData/Repository.testGetHookDeliveries.txt @@ -0,0 +1,10 @@ +https +GET +api.github.com +None +/repos/jacquev6/PyGithub/hooks/257993/deliveries +{'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python'} +None +200 +[('status', '200 OK'), ('x-ratelimit-remaining', '4953'), ('content-length', '295'), ('server', 'nginx/1.0.13'), ('connection', 'keep-alive'), ('x-ratelimit-limit', '5000'), ('etag', '"07e5e1a2fafd1a5e2de62eb3afd007d5"'), ('date', 'Sun, 27 May 2012 07:02:25 GMT'), ('content-type', 'application/json; charset=utf-8')] +[{"id":12345,"guid":"abcde-12345","delivered_at":"2012-05-27T06:00:32Z","redelivery":false,"duration":0.27,"status":"OK","status_code":200,"event":"issues","action":"opened","installation_id":123,"repository_id":456,"url":"https://www.example-webhook.com"}] diff --git a/tests/ReplayData/Repository.testGetHookDelivery.txt b/tests/ReplayData/Repository.testGetHookDelivery.txt new file mode 100644 index 0000000000..67029a72d6 --- /dev/null +++ b/tests/ReplayData/Repository.testGetHookDelivery.txt @@ -0,0 +1,10 @@ +https +GET +api.github.com +None +/repos/jacquev6/PyGithub/hooks/257993/deliveries/12345 +{'Authorization': 'Basic login_and_password_removed', 'User-Agent': 'PyGithub/Python'} +None +200 +[('status', '200 OK'), ('x-ratelimit-remaining', '4953'), ('content-length', '295'), ('server', 'nginx/1.0.13'), ('connection', 'keep-alive'), ('x-ratelimit-limit', '5000'), ('etag', '"07e5e1a2fafd1a5e2de62eb3afd007d5"'), ('date', 'Sun, 27 May 2012 07:02:25 GMT'), ('content-type', 'application/json; charset=utf-8')] +{"id":12345,"guid":"abcde-12345","delivered_at":"2012-05-27T06:00:32Z","redelivery":false,"duration":0.27,"status":"OK","status_code":200,"event":"issues","action":"opened","installation_id":123,"repository_id":456,"url":"https://www.example-webhook.com","request":{"headers":{"content-type": "application/json"},"payload":{"action": "opened"}},"response":{"headers":{"content-type": "text/html;charset=utf-8"},"payload":"ok"}} diff --git a/tests/Repository.py b/tests/Repository.py index 1f999c1d4c..d843c7dc5f 100644 --- a/tests/Repository.py +++ b/tests/Repository.py @@ -932,6 +932,15 @@ def testGetGitTreeWithRecursive(self): def testGetHooks(self): self.assertListKeyEqual(self.repo.get_hooks(), lambda h: h.id, [257993]) + def testGetHookDelivery(self): + delivery = self.repo.get_hook_delivery(257993, 12345) + self.assertEqual(delivery.id, 12345) + + def testGetHookDeliveries(self): + self.assertListKeyEqual( + self.repo.get_hook_deliveries(257993), lambda d: d.id, [12345] + ) + def testGetIssues(self): self.assertListKeyEqual( self.repo.get_issues(),