async def main(request: Request) -> Response: try: body = await request.read() secret = os.environ.get("GITHUB_SECRET") event = Event.from_http(request.headers, body, secret=secret) if event.event == "ping": return Response(status=200) logger.info( "event=%s delivery_id=%s", f"{event.event}:{event.data['action']}", event.delivery_id, ) async with ClientSession() as session: gh = GitHubAPI( event.data["installation"]["id"], session, "dhruvmanila/algorithms-keeper", cache=cache, ) # Give GitHub some time to reach internal consistency. await asyncio.sleep(1) await main_router.dispatch(event, gh) if gh.rate_limit is not None: # pragma: no cover logger.info( "ratelimit=%s, time_remaining=%s", f"{gh.rate_limit.remaining}/{gh.rate_limit.limit}", gh.rate_limit.reset_datetime - datetime.now(timezone.utc), ) return Response(status=200) except Exception as err: logger.exception(err) return Response(status=500, text=str(err))
async def event_from_request(self, request): """Get an event object out of HTTP request.""" event = Event.from_http( request.headers, await request.read(), secret=self._config.webhook_secret, ) await self.pre_process_webhook_event(event) return event
async def _dispatch(self, request): try: event = Event.from_http(request.headers, await request.read(), secret=self._verification) await self._github_router.dispatch(event) except Exception as e: logger.exception(e) return Response(status=500) else: return Response(status=200)
def test_process_webhook_payload(incoming_event, is_successful): """Test that @process_webhook_payload unpacks event into kw-args.""" event = Event(data=incoming_event, event=None, delivery_id=None) if is_successful: assert ( # pylint: disable=missing-kwoa,too-many-function-args fake_event_handler(event) == tuple(incoming_event.values())) else: with pytest.raises(TypeError): # pylint: disable=missing-kwoa,too-many-function-args fake_event_handler(event)
async def review(event: Event, gh: GitHubAPI, *args: Any, **kwargs: Any) -> None: """Review command to trigger the checks for all the pull request files.""" issue = event.data["issue"] comment = event.data["comment"] if "pull_request" in issue: # Give a heads up that the command has been received. await utils.add_reaction(gh, reaction="+1", comment=comment) event.data["pull_request"] = await utils.get_pr_for_issue(gh, issue=issue) await check_pr_files(event, gh, *args, **kwargs) else: # The command cannot be run on an issue. await utils.add_reaction(gh, reaction="-1", comment=comment)
def event(self): # noqa: D401 """Parsed GitHub Action event data.""" try: # NOTE: This could be async but it probably doesn't matter # NOTE: since it's called just once during init and GitHub # NOTE: Action runtime only has one event to process # pylint: disable=no-member with self._metadata.event_path.open() as event_source: return Event( json.load(event_source), event=self._metadata.event_name, delivery_id=uuid4(), ) except TypeError: return None
async def dispatch(request): github = request.app.plugins['github'] payload = await request.read() try: event = Event.from_http(request.headers, payload, secret=github.verify) await github.router.dispatch(event, app=request.app) except ValidationFailure as e: LOG.debug('Github webhook failed verification: %s, %s', request.headers, payload) return Response(status=401) except Exception as e: LOG.exception(e) return Response(status=500) else: return Response(status=200)
async def dispatch_webhook(self, headers, body): event = Event.from_http(headers, body, secret=self.webhook_secret) print( f"GH webhook received: type={event.event}, delivery id={event.delivery_id}" ) # Wait a bit to give Github's eventual consistency time to catch up await anyio.sleep(1) installation_id = glom(event.data, "installation.id", default=None) if installation_id is None: print("No associated installation; not dispatching") return client = self.client_for(installation_id) for route in self._routes[event.event]: if _all_match(event.data, route.restrictions): print(f"Routing to {route.async_fn!r}") await route.async_fn(event.event, event.data, client) try: limit = client.rate_limit.remaining except AttributeError: pass else: print(f"Rate limit for install {installation_id}: {limit}")
async def main(request: web.Request) -> web.Response: try: body = await request.read() secret = os.environ.get("GITHUB_SECRET") event = Event.from_http(request.headers, body, secret=secret) if event.event == "ping": return web.Response(status=200) logger.info( "event=%(event)s delivery_id=%(delivery_id)s", { "event": f"{event.event}:{event.data['action']}", "delivery_id": event.delivery_id, }, ) async with aiohttp.ClientSession() as session: gh = GitHubAPI(session, "dhruvmanila/algorithms-keeper", cache=cache) # Give GitHub some time to reach internal consistency. await asyncio.sleep(1) await router.dispatch(event, gh) try: logger.info( "ratelimit=%(ratelimit)s time_remaining=%(time_remaining)s", { "ratelimit": f"{gh.rate_limit.remaining}/{gh.rate_limit.limit}", "time_remaining": gh.rate_limit.reset_datetime - datetime.datetime.now(datetime.timezone.utc), }, ) except AttributeError: pass return web.Response(status=200) except Exception as err: logger.exception(err) return web.Response(status=500, text=str(err))
# Reminder: ``Event.delivery_id`` is used as a short description for the respective # test case and as a way to id the specific test case in the parametrized group. @pytest.mark.asyncio @pytest.mark.parametrize( "event, gh, expected", ( # Installation was created on a repository. ( Event( data={ "action": "created", "installation": { "id": number }, "repositories": [{ "full_name": repository }], "sender": { "login": user }, }, event="installation", delivery_id="installation_created", ), MockGitHubAPI(post={issue_path: { "url": issue_url }}), ExpectedData( post_url=[issue_path], post_data=[{ "title": "Installation successful!", "body": FILLED_GREETING_COMMENT,
assert match.group(1) == group # Reminder: ``Event.delivery_id`` is used as a short description for the respective # test case and as a way to id the specific test case in the parametrized group. @pytest.mark.asyncio @pytest.mark.parametrize( "event, gh, expected", ( # Issue comment made by a non member, so do nothing. ( Event( data={ "action": "created", "comment": { "author_association": "NONE", }, }, event="issue_comment", delivery_id="non_member_commented", ), MockGitHubAPI(), ExpectedData(), ), # Issue comment came from an issue instead of a pull request, so do nothing. ( Event( data={ "action": "created", "comment": { "url": comment_url, "author_association": "MEMBER",
# test case and as a way to id the specific test case in the parametrized group. @pytest.mark.asyncio @pytest.mark.parametrize( "event, gh, expected", ( # Issue opened with an empty description so label it invalid, comment and # close the issue. ( Event( data={ "action": "opened", "issue": { "url": issue_url, "comments_url": comments_url, "labels_url": labels_url, "user": {"login": user}, "body": "", "html_url": html_issue_url, }, }, event="issues", delivery_id="empty_description", ), MockGitHubAPI(), ExpectedData( post_url=[comments_url, labels_url], post_data=[ {"body": comment}, {"labels": [Label.INVALID]}, ], patch_url=[issue_url],
Event( data={ "action": "opened", "pull_request": { "url": pr_url, "body": CHECKBOX_TICKED_UPPER, # Case doesn't matter "user": { "login": user }, "labels": [], "author_association": "NONE", "comments_url": comments_url, "issue_url": issue_url, "html_url": html_pr_url, "requested_reviewers": [{ "login": "******" }, { "login": "******" }], "draft": False, "mergeable": True, }, "repository": { "full_name": repository }, }, event="pull_request", delivery_id=MAX_PR_TEST_ENABLED_ID, ),
# Reminder: ``Event.delivery_id`` is used as a short description for the respective # test case and as a way to id the specific test case in the parametrized group. @pytest.mark.asyncio @pytest.mark.parametrize( "event, gh, expected", ( # Check run completed from a commit which does not belong to a pull request. ( Event( data={ "action": "completed", "check_run": { "head_sha": sha, }, "repository": { "full_name": repository }, }, event="check_run", delivery_id="commit_not_from_pr", ), MockGitHubAPI( getitem={search_url: { "total_count": 0, "items": [], }}), ExpectedData(getitem_url=[search_url]), ), # Check run completed but some of the other checks are in progress. (