From 838a4e12ef32f7292578f307be4eb712b28d2f3b Mon Sep 17 00:00:00 2001 From: Marc Durepos Date: Sun, 23 Jun 2024 07:14:32 -0400 Subject: [PATCH] caldav_sync: fix a dictionary update error during sync. Exception handling was also changed to only log the errors rather than to raise them to the user, since the sync is run as a cron and that we want other calendars to be polled if one fails. --- caldav_sync/__manifest__.py | 2 +- caldav_sync/models/calendar_event.py | 30 +++--- caldav_sync/tests/test_caldav_sync.py | 149 +++++++++++++++----------- 3 files changed, 102 insertions(+), 79 deletions(-) diff --git a/caldav_sync/__manifest__.py b/caldav_sync/__manifest__.py index af4b4ca..e7242cf 100644 --- a/caldav_sync/__manifest__.py +++ b/caldav_sync/__manifest__.py @@ -8,7 +8,7 @@ { "name": "CalDAV Synchronization", - "version": "17.0.0.5.4", + "version": "17.0.0.5.5", "license": "LGPL-3", "category": "Productivity", "summary": "Synchronize Odoo Calendar Events with CalDAV Servers", diff --git a/caldav_sync/models/calendar_event.py b/caldav_sync/models/calendar_event.py index 77bb627..1dec89d 100644 --- a/caldav_sync/models/calendar_event.py +++ b/caldav_sync/models/calendar_event.py @@ -211,18 +211,22 @@ class CalendarEvent(models.Model): calendar = client.calendar(url=self.env.user.caldav_calendar_url) events = calendar.events() except Exception as e: - _logger.warning(e) - principal = client.principal() - msg = f"""Failed to connect to the calendar, but successfully connected to the -server at {client.url}. -You may need to select another calendar URL from those below. - -Available calendars: - -""" - for calendar in principal.calendars(): - msg += f"{calendar.name}: {calendar.url}\n" - raise UserError(msg) + _logger.error(e) + try: + principal = client.principal() + msg = f"""Failed to connect to the calendar, but successfully connected to the + server at {client.url}. + You may need to select another calendar URL from those below. + + Available calendars: + + """ + for calendar in principal.calendars(): + msg += f"{calendar.name}: {calendar.url}\n" + raise UserError(msg) + except Exception as e: + _logger.error(e) + return caldav_uids = set() _logger.info(f"Polling CalDAV server for user {self.env.user.name}") @@ -394,7 +398,7 @@ Available calendars: # Don't write values that haven't changed for key, val in values.items(): if getattr(existing_instance, key) != val: - changed_vals.update({key: values.pop(key)}) + changed_vals.update({key: values.get(key)}) if ( recurrency_vals and recurrency_vals.get("recurrency") diff --git a/caldav_sync/tests/test_caldav_sync.py b/caldav_sync/tests/test_caldav_sync.py index 38e2c27..15cb617 100644 --- a/caldav_sync/tests/test_caldav_sync.py +++ b/caldav_sync/tests/test_caldav_sync.py @@ -12,16 +12,20 @@ class TestCaldavSync(TransactionCase): def setUp(self): super(TestCaldavSync, self).setUp() - self.user = self.env['res.users'].create({ - 'name': 'Test User', - 'login': 'testuser', - 'caldav_calendar_url': 'http://test.calendar.url', - 'caldav_username': 'testuser', - 'caldav_password': 'password', - }) + self.user = self.env["res.users"].create( + { + "name": "Test User", + "login": "testuser", + "caldav_calendar_url": "http://test.calendar.url", + "caldav_username": "testuser", + "caldav_password": "password", + } + ) self.env = self.env(context=dict(self.env.context, no_reset_password=True)) - @patch('odoo.addons.caldav_sync.models.calendar_event.CalendarEvent._get_caldav_client') + @patch( + "odoo.addons.caldav_sync.models.calendar_event.CalendarEvent._get_caldav_client" + ) def test_create_caldav_event(self, mock_get_caldav_client): mock_client = MagicMock() mock_calendar = MagicMock() @@ -30,7 +34,7 @@ class TestCaldavSync(TransactionCase): caldav_event = MagicMock() cal = Calendar.from_ical(ical_event) ical_event_instance = next(iter(cal.subcomponents)) - caldav_event.vobject_instance.vevent.uid.value = ical_event_instance['UID'] + caldav_event.vobject_instance.vevent.uid.value = ical_event_instance["UID"] return caldav_event mock_calendar.add_event.side_effect = add_event_side_effect @@ -38,56 +42,72 @@ class TestCaldavSync(TransactionCase): mock_client.calendar.return_value = mock_calendar event_data = { - 'name': 'Test Event', - 'start': '2024-05-22 10:00:00', - 'stop': '2024-05-22 11:00:00', - 'description': '

This is a test event

', - 'location': 'Test Location', + "name": "Test Event", + "start": "2024-05-22 10:00:00", + "stop": "2024-05-22 11:00:00", + "description": "This is a test event", + "location": "Test Location", } - event = self.env['calendar.event'].with_user(self.user).create(event_data) + event = self.env["calendar.event"].with_user(self.user).create(event_data) cal = Calendar.from_ical(mock_calendar.add_event.call_args[0][0]) ical_event = next(iter(cal.subcomponents)) - self.assertEqual(str(ical_event.get('summary')), event_data['name']) - self.assertEqual(str(ical_event.get('location')), event_data['location']) - self.assertEqual(ical_event.get('description'), event_data['description']) + self.assertEqual(str(ical_event.get("summary")), event_data["name"]) + self.assertEqual(str(ical_event.get("location")), event_data["location"]) + self.assertEqual(ical_event.get("description"), event_data["description"]) self.assertIsNotNone(event.caldav_uid) - self.assertEqual(event.caldav_uid, ical_event['UID']) + self.assertEqual(event.caldav_uid, ical_event["UID"]) - @patch('odoo.addons.caldav_sync.models.calendar_event.CalendarEvent._get_caldav_client') - @patch('odoo.addons.caldav_sync.models.calendar_event.CalendarEvent.sync_update_to_caldav') - def test_update_caldav_event(self, mock_sync_update_to_caldav, mock_get_caldav_client): + @patch( + "odoo.addons.caldav_sync.models.calendar_event.CalendarEvent._get_caldav_client" + ) + @patch( + "odoo.addons.caldav_sync.models.calendar_event.CalendarEvent.sync_update_to_caldav" + ) + def test_update_caldav_event( + self, mock_sync_update_to_caldav, mock_get_caldav_client + ): mock_client = MagicMock() mock_calendar = MagicMock() mock_event = MagicMock() - mock_event.id = 'test-uid-12345' + mock_event.id = "test-uid-12345" mock_client.calendar.return_value = mock_calendar mock_calendar.add_event.return_value = mock_event mock_get_caldav_client.return_value = mock_client - event = self.env['calendar.event'].with_user(self.user).create({ - 'name': 'Test Event', - 'start': '2024-05-22 10:00:00', - 'stop': '2024-05-22 11:00:00', - 'description': 'This is a test event', - 'location': 'Test Location', - 'create_uid': self.user.id, - }) + event = ( + self.env["calendar.event"] + .with_user(self.user) + .create( + { + "name": "Test Event", + "start": "2024-05-22 10:00:00", + "stop": "2024-05-22 11:00:00", + "description": "This is a test event", + "location": "Test Location", + "create_uid": self.user.id, + } + ) + ) - event.with_user(self.user).write({ - 'name': 'Updated Test Event', - 'start': '2024-05-22 12:00:00', - 'stop': '2024-05-22 13:00:00', - }) + event.with_user(self.user).write( + { + "name": "Updated Test Event", + "start": "2024-05-22 12:00:00", + "stop": "2024-05-22 13:00:00", + } + ) mock_sync_update_to_caldav.assert_called_once() - self.assertEqual(event.name, 'Updated Test Event') + self.assertEqual(event.name, "Updated Test Event") self.assertEqual(event.start, datetime(2024, 5, 22, 12, 0)) - @patch('odoo.addons.caldav_sync.models.calendar_event.CalendarEvent._get_caldav_client') + @patch( + "odoo.addons.caldav_sync.models.calendar_event.CalendarEvent._get_caldav_client" + ) def test_delete_caldav_event(self, mock_get_caldav_client): mock_client = MagicMock() mock_calendar = MagicMock() @@ -97,24 +117,32 @@ class TestCaldavSync(TransactionCase): mock_calendar.object_by_uid.return_value = mock_event mock_get_caldav_client.return_value = mock_client - event = self.env['calendar.event'].with_user(self.user).create({ - 'name': 'Test Event', - 'start': '2024-05-22 10:00:00', - 'stop': '2024-05-22 11:00:00', - 'description': 'This is a test event', - 'location': 'Test Location', - 'create_uid': self.user.id, - }) + event = ( + self.env["calendar.event"] + .with_user(self.user) + .create( + { + "name": "Test Event", + "start": "2024-05-22 10:00:00", + "stop": "2024-05-22 11:00:00", + "description": "This is a test event", + "location": "Test Location", + "create_uid": self.user.id, + } + ) + ) uid = event.caldav_uid event.with_user(self.user).unlink() mock_calendar.object_by_uid.assert_called_once_with(uid) mock_event.delete.assert_called_once() - @patch('odoo.addons.caldav_sync.models.calendar_event.CalendarEvent.sync_event_from_ical') + @patch( + "odoo.addons.caldav_sync.models.calendar_event.CalendarEvent.sync_event_from_ical" + ) def test_poll_caldav_server(self, mock_sync_event_from_ical): mock_sync_event_from_ical.return_value = None - with patch('caldav.DAVClient') as MockClient: + with patch("caldav.DAVClient") as MockClient: mock_client = MockClient.return_value mock_calendar = mock_client.calendar.return_value mock_event = MagicMock() @@ -122,27 +150,18 @@ class TestCaldavSync(TransactionCase): # Create a Calendar object and add an Event to it cal = Calendar() event = Event() - event.add('uid', 'test-uid-12345') - event.add('dtstamp', datetime(2024, 5, 22, 10, 0, 0)) - event.add('dtstart', datetime(2024, 5, 22, 10, 0, 0)) - event.add('dtend', datetime(2024, 5, 22, 11, 0, 0)) - event.add('summary', 'Polled Event') - event.add('description', 'This event was polled from CalDAV') - event.add('location', 'Polled Location') + event.add("uid", "test-uid-12345") + event.add("dtstamp", datetime(2024, 5, 22, 10, 0, 0)) + event.add("dtstart", datetime(2024, 5, 22, 10, 0, 0)) + event.add("dtend", datetime(2024, 5, 22, 11, 0, 0)) + event.add("summary", "Polled Event") + event.add("description", "This event was polled from CalDAV") + event.add("location", "Polled Location") cal.add_component(event) # Set the mock event's icalendar_instance to the iCal string mock_event.icalendar_instance = Calendar.from_ical(cal.to_ical()) mock_calendar.events.return_value = [mock_event] - self.env['calendar.event'].poll_caldav_server() + self.env["calendar.event"].poll_caldav_server() mock_sync_event_from_ical.assert_called_once() - - @patch('odoo.addons.caldav_sync.models.calendar_event._logger') - def test_poll_caldav_server_with_exception(self, mock_logger): - with patch('caldav.DAVClient') as MockClient: - mock_client = MockClient.return_value - mock_client.calendar.side_effect = Exception('Invalid credentials') - - self.env['calendar.event'].poll_caldav_server() - mock_logger.error.assert_any_call('Failed to poll CalDAV server for user Test User: Invalid credentials')