diff --git a/homeassistant/components/google/calendar.py b/homeassistant/components/google/calendar.py index 2339dc3f65d..b0d13c0c0c6 100644 --- a/homeassistant/components/google/calendar.py +++ b/homeassistant/components/google/calendar.py @@ -158,7 +158,6 @@ class GoogleCalendarEntity(CalendarEntity): self, hass: HomeAssistant, start_date: datetime, end_date: datetime ) -> list[CalendarEvent]: """Get all events in a specific time frame.""" - event_list: list[dict[str, Any]] = [] request = ListEventsRequest( calendar_id=self._calendar_id, @@ -166,20 +165,18 @@ class GoogleCalendarEntity(CalendarEntity): end_time=end_date, search=self._search, ) - while True: - try: - result = await self._calendar_service.async_list_events(request) - except ApiException as err: - _LOGGER.error("Unable to connect to Google: %s", err) - return [] - - event_list.extend(filter(self._event_filter, result.items)) - if not result.page_token: - break - - request.page_token = result.page_token - - return [_get_calendar_event(event) for event in event_list] + result_items = [] + try: + result = await self._calendar_service.async_list_events(request) + async for result_page in result: + result_items.extend(result_page.items) + except ApiException as err: + _LOGGER.error("Unable to connect to Google: %s", err) + return [] + return [ + _get_calendar_event(event) + for event in filter(self._event_filter, result_items) + ] @Throttle(MIN_TIME_BETWEEN_UPDATES) async def async_update(self) -> None: diff --git a/homeassistant/components/google/manifest.json b/homeassistant/components/google/manifest.json index eff69befb37..8af1fa00437 100644 --- a/homeassistant/components/google/manifest.json +++ b/homeassistant/components/google/manifest.json @@ -4,7 +4,7 @@ "config_flow": true, "dependencies": ["auth"], "documentation": "https://www.home-assistant.io/integrations/calendar.google/", - "requirements": ["gcal-sync==0.5.0", "oauth2client==4.1.3"], + "requirements": ["gcal-sync==0.6.2", "oauth2client==4.1.3"], "codeowners": ["@allenporter"], "iot_class": "cloud_polling", "loggers": ["googleapiclient"] diff --git a/requirements_all.txt b/requirements_all.txt index 83780908085..daa9ae78610 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -689,7 +689,7 @@ gTTS==2.2.4 garages-amsterdam==3.0.0 # homeassistant.components.google -gcal-sync==0.5.0 +gcal-sync==0.6.2 # homeassistant.components.geniushub geniushub-client==0.6.30 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 7621b7adb3a..9975c08b5df 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -486,7 +486,7 @@ gTTS==2.2.4 garages-amsterdam==3.0.0 # homeassistant.components.google -gcal-sync==0.5.0 +gcal-sync==0.6.2 # homeassistant.components.usgs_earthquakes_feed geojson_client==0.6 diff --git a/tests/components/google/test_calendar.py b/tests/components/google/test_calendar.py index a5721462e7c..6ae62f50914 100644 --- a/tests/components/google/test_calendar.py +++ b/tests/components/google/test_calendar.py @@ -2,6 +2,7 @@ from __future__ import annotations +import copy import datetime from http import HTTPStatus from typing import Any @@ -9,15 +10,17 @@ from unittest.mock import patch import urllib from aiohttp.client_exceptions import ClientError +from gcal_sync.auth import API_BASE_URL import pytest from homeassistant.const import STATE_OFF, STATE_ON from homeassistant.helpers.template import DATE_STR_FORMAT import homeassistant.util.dt as dt_util -from .conftest import TEST_YAML_ENTITY, TEST_YAML_ENTITY_NAME +from .conftest import CALENDAR_ID, TEST_YAML_ENTITY, TEST_YAML_ENTITY_NAME from tests.common import async_fire_time_changed +from tests.test_util.aiohttp import AiohttpClientMockResponse TEST_ENTITY = TEST_YAML_ENTITY TEST_ENTITY_NAME = TEST_YAML_ENTITY_NAME @@ -471,6 +474,66 @@ async def test_http_api_all_day_event( } +@pytest.mark.freeze_time("2022-03-27 12:05:00+00:00") +async def test_http_api_event_paging( + hass, hass_client, aioclient_mock, component_setup +): + """Test paging through results from the server.""" + hass.config.set_time_zone("Asia/Baghdad") + + responses = [ + { + "nextPageToken": "page-token", + "items": [ + { + **TEST_EVENT, + "summary": "event 1", + **upcoming(), + } + ], + }, + { + "items": [ + { + **TEST_EVENT, + "summary": "event 2", + **upcoming(), + } + ], + }, + ] + + def next_response(response_list): + results = copy.copy(response_list) + + async def get(method, url, data): + return AiohttpClientMockResponse(method, url, json=results.pop(0)) + + return get + + # Setup response for initial entity load + aioclient_mock.get( + f"{API_BASE_URL}/calendars/{CALENDAR_ID}/events", + side_effect=next_response(responses), + ) + assert await component_setup() + + # Setup response for API request + aioclient_mock.clear_requests() + aioclient_mock.get( + f"{API_BASE_URL}/calendars/{CALENDAR_ID}/events", + side_effect=next_response(responses), + ) + + client = await hass_client() + response = await client.get(upcoming_event_url()) + assert response.status == HTTPStatus.OK + events = await response.json() + assert len(events) == 2 + assert events[0]["summary"] == "event 1" + assert events[1]["summary"] == "event 2" + + @pytest.mark.parametrize( "calendars_config_ignore_availability,transparency,expect_visible_event", [