diff --git a/addons/microsoft_calendar/tests/test_microsoft_service.py b/addons/microsoft_calendar/tests/test_microsoft_service.py index 471b954f8db..2bcfe022f5c 100644 --- a/addons/microsoft_calendar/tests/test_microsoft_service.py +++ b/addons/microsoft_calendar/tests/test_microsoft_service.py @@ -62,8 +62,9 @@ class TestMicrosoftService(TransactionCase): with self.assertRaises(Exception): self.service._get_events_delta(token=self.fake_token, timeout=DEFAULT_TIMEOUT) + @patch.object(MicrosoftCalendarService, "_check_full_sync_required") @patch.object(MicrosoftService, "_do_request") - def test_get_events_delta_token_error(self, mock_do_request): + def test_get_events_delta_token_error(self, mock_do_request, mock_check_full_sync_required): """ When the provided sync token is invalid, an exception should be raised and then a full sync should be done. @@ -72,6 +73,7 @@ class TestMicrosoftService(TransactionCase): requests.HTTPError(response=MagicMock(status_code=410, content="fullSyncRequired")), self._do_request_result({"value": []}), ] + mock_check_full_sync_required.return_value = (True) events, next_token = self.service._get_events_delta( token=self.fake_token, sync_token=self.fake_sync_token, timeout=DEFAULT_TIMEOUT @@ -437,3 +439,25 @@ class TestMicrosoftService(TransactionCase): json.dumps(values), self.header, method="POST", timeout=DEFAULT_TIMEOUT ) + + @patch.object(MicrosoftCalendarService, "_check_full_sync_required") + @patch.object(MicrosoftService, "_do_request") + def test_get_events_delta_with_outdated_sync_token(self, mock_do_request, mock_check_full_sync_required): + """ When an outdated sync token is provided, we must fetch all events again for updating the old token. """ + # Throw a 'HTTPError' when the token is outdated, thus triggering the fetching of all events. + # Simulate a scenario which the full sync is required, such as when getting the 'SyncStateNotFound' error code. + mock_do_request.side_effect = [ + requests.HTTPError(response=MagicMock(status_code=410, error={'code': "SyncStateNotFound"})), + self._do_request_result({"value": []}), + ] + mock_check_full_sync_required.return_value = (True) + + # Call the regular 'delta' get events with an outdated token for triggering the all events fetching. + self.env.user.microsoft_calendar_sync_token = self.fake_sync_token + self.service._get_events_delta(token=self.fake_token, sync_token=self.fake_sync_token, timeout=DEFAULT_TIMEOUT) + + # Two calls must have been made: one call with the outdated sync token and another one with no sync token. + mock_do_request.assert_has_calls([ + self.call_with_sync_token, + self.call_without_sync_token + ]) diff --git a/addons/microsoft_calendar/utils/microsoft_calendar.py b/addons/microsoft_calendar/utils/microsoft_calendar.py index 8882b0592a8..62cb416b09a 100644 --- a/addons/microsoft_calendar/utils/microsoft_calendar.py +++ b/addons/microsoft_calendar/utils/microsoft_calendar.py @@ -92,6 +92,12 @@ class MicrosoftCalendarService(): return events, next_sync_token + def _check_full_sync_required(self, response): + """ Checks if full sync is required according to the error code received. """ + response_json = response.json() + response_code = response_json.get('error', {}).get('code', '') + return any(error_code in response_code for error_code in ['fullSyncRequired', 'SyncStateNotFound']) + @requires_auth_token def _get_events_delta(self, sync_token=None, token=None, timeout=TIMEOUT): """ @@ -105,7 +111,8 @@ class MicrosoftCalendarService(): events, next_sync_token = self._get_events_from_paginated_url( url, params=params, token=token, timeout=timeout) except requests.HTTPError as e: - if e.response.status_code == 410 and 'fullSyncRequired' in str(e.response.content) and sync_token: + full_sync_needed = self._check_full_sync_required(e.response) + if e.response.status_code == 410 and full_sync_needed and sync_token: # retry with a full sync return self._get_events_delta(token=token, timeout=timeout) raise e