Repository event subscription (#67284)

This commit is contained in:
Joakim Sørensen 2022-02-26 15:56:36 +01:00 committed by GitHub
parent 73fdd47d54
commit e65670fef4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 61 additions and 7 deletions

View File

@ -38,6 +38,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
)
await coordinator.async_config_entry_first_refresh()
await coordinator.subscribe()
hass.data[DOMAIN][repository] = coordinator
@ -45,7 +46,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
entry.async_on_unload(entry.add_update_listener(async_reload_entry))
return True
@ -77,6 +77,10 @@ def async_cleanup_device_registry(
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
repositories: dict[str, GitHubDataUpdateCoordinator] = hass.data[DOMAIN]
for coordinator in repositories.values():
coordinator.unsubscribe()
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
hass.data.pop(DOMAIN)
return unload_ok

View File

@ -11,7 +11,18 @@ DOMAIN = "github"
CLIENT_ID = "1440cafcc86e3ea5d6a2"
DEFAULT_REPOSITORIES = ["home-assistant/core", "esphome/esphome"]
DEFAULT_UPDATE_INTERVAL = timedelta(seconds=300)
FALLBACK_UPDATE_INTERVAL = timedelta(hours=1, minutes=30)
CONF_ACCESS_TOKEN = "access_token"
CONF_REPOSITORIES = "repositories"
REFRESH_EVENT_TYPES = (
"CreateEvent",
"ForkEvent",
"IssuesEvent",
"PullRequestEvent",
"PushEvent",
"ReleaseEvent",
"WatchEvent",
)

View File

@ -6,15 +6,17 @@ from typing import Any
from aiogithubapi import (
GitHubAPI,
GitHubConnectionException,
GitHubEventModel,
GitHubException,
GitHubRatelimitException,
GitHubResponseModel,
)
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import DEFAULT_UPDATE_INTERVAL, LOGGER
from .const import FALLBACK_UPDATE_INTERVAL, LOGGER, REFRESH_EVENT_TYPES
GRAPHQL_REPOSITORY_QUERY = """
query ($owner: String!, $repository: String!) {
@ -109,13 +111,14 @@ class GitHubDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
self.repository = repository
self._client = client
self._last_response: GitHubResponseModel[dict[str, Any]] | None = None
self._subscription_id: str | None = None
self.data = {}
super().__init__(
hass,
LOGGER,
name=repository,
update_interval=DEFAULT_UPDATE_INTERVAL,
update_interval=FALLBACK_UPDATE_INTERVAL,
)
async def _async_update_data(self) -> GitHubResponseModel[dict[str, Any]]:
@ -136,3 +139,26 @@ class GitHubDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
else:
self._last_response = response
return response.data["data"]["repository"]
async def _handle_event(self, event: GitHubEventModel) -> None:
"""Handle an event."""
if event.type in REFRESH_EVENT_TYPES:
await self.async_request_refresh()
@staticmethod
async def _handle_error(error: GitHubException) -> None:
"""Handle an error."""
LOGGER.error("An error occurred while processing new events - %s", error)
async def subscribe(self) -> None:
"""Subscribe to repository events."""
self._subscription_id = await self._client.repos.events.subscribe(
self.repository,
event_callback=self._handle_event,
error_callback=self._handle_error,
)
self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self.unsubscribe)
def unsubscribe(self, *args) -> None:
"""Unsubscribe to repository events."""
self._client.repos.events.unsubscribe(subscription_id=self._subscription_id)

View File

@ -31,6 +31,11 @@ async def setup_github_integration(
},
headers=headers,
)
aioclient_mock.get(
f"https://api.github.com/repos/{repository}/events",
json=[],
headers=headers,
)
aioclient_mock.post(
"https://api.github.com/graphql",
json=json.loads(load_fixture("graphql.json", DOMAIN)),

View File

@ -1,10 +1,12 @@
"""Test GitHub sensor."""
import json
from homeassistant.components.github.const import DEFAULT_UPDATE_INTERVAL, DOMAIN
from homeassistant.components.github.const import DOMAIN, FALLBACK_UPDATE_INTERVAL
from homeassistant.core import HomeAssistant
from homeassistant.util import dt
from .common import TEST_REPOSITORY
from tests.common import MockConfigEntry, async_fire_time_changed, load_fixture
from tests.test_util.aiohttp import AiohttpClientMocker
@ -22,15 +24,21 @@ async def test_sensor_updates_with_empty_release_array(
response_json = json.loads(load_fixture("graphql.json", DOMAIN))
response_json["data"]["repository"]["release"] = None
headers = json.loads(load_fixture("base_headers.json", DOMAIN))
aioclient_mock.clear_requests()
aioclient_mock.get(
f"https://api.github.com/repos/{TEST_REPOSITORY}/events",
json=[],
headers=headers,
)
aioclient_mock.post(
"https://api.github.com/graphql",
json=response_json,
headers=json.loads(load_fixture("base_headers.json", DOMAIN)),
headers=headers,
)
async_fire_time_changed(hass, dt.utcnow() + DEFAULT_UPDATE_INTERVAL)
async_fire_time_changed(hass, dt.utcnow() + FALLBACK_UPDATE_INTERVAL)
await hass.async_block_till_done()
new_state = hass.states.get(TEST_SENSOR_ENTITY)