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.
This commit is contained in:
parent
92056ac236
commit
838a4e12ef
3 changed files with 102 additions and 79 deletions
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "CalDAV Synchronization",
|
"name": "CalDAV Synchronization",
|
||||||
"version": "17.0.0.5.4",
|
"version": "17.0.0.5.5",
|
||||||
"license": "LGPL-3",
|
"license": "LGPL-3",
|
||||||
"category": "Productivity",
|
"category": "Productivity",
|
||||||
"summary": "Synchronize Odoo Calendar Events with CalDAV Servers",
|
"summary": "Synchronize Odoo Calendar Events with CalDAV Servers",
|
||||||
|
|
|
||||||
|
|
@ -211,18 +211,22 @@ class CalendarEvent(models.Model):
|
||||||
calendar = client.calendar(url=self.env.user.caldav_calendar_url)
|
calendar = client.calendar(url=self.env.user.caldav_calendar_url)
|
||||||
events = calendar.events()
|
events = calendar.events()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
_logger.warning(e)
|
_logger.error(e)
|
||||||
principal = client.principal()
|
try:
|
||||||
msg = f"""Failed to connect to the calendar, but successfully connected to the
|
principal = client.principal()
|
||||||
server at {client.url}.
|
msg = f"""Failed to connect to the calendar, but successfully connected to the
|
||||||
You may need to select another calendar URL from those below.
|
server at {client.url}.
|
||||||
|
You may need to select another calendar URL from those below.
|
||||||
Available calendars:
|
|
||||||
|
Available calendars:
|
||||||
"""
|
|
||||||
for calendar in principal.calendars():
|
"""
|
||||||
msg += f"{calendar.name}: {calendar.url}\n"
|
for calendar in principal.calendars():
|
||||||
raise UserError(msg)
|
msg += f"{calendar.name}: {calendar.url}\n"
|
||||||
|
raise UserError(msg)
|
||||||
|
except Exception as e:
|
||||||
|
_logger.error(e)
|
||||||
|
return
|
||||||
caldav_uids = set()
|
caldav_uids = set()
|
||||||
|
|
||||||
_logger.info(f"Polling CalDAV server for user {self.env.user.name}")
|
_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
|
# Don't write values that haven't changed
|
||||||
for key, val in values.items():
|
for key, val in values.items():
|
||||||
if getattr(existing_instance, key) != val:
|
if getattr(existing_instance, key) != val:
|
||||||
changed_vals.update({key: values.pop(key)})
|
changed_vals.update({key: values.get(key)})
|
||||||
if (
|
if (
|
||||||
recurrency_vals
|
recurrency_vals
|
||||||
and recurrency_vals.get("recurrency")
|
and recurrency_vals.get("recurrency")
|
||||||
|
|
|
||||||
|
|
@ -12,16 +12,20 @@ class TestCaldavSync(TransactionCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestCaldavSync, self).setUp()
|
super(TestCaldavSync, self).setUp()
|
||||||
self.user = self.env['res.users'].create({
|
self.user = self.env["res.users"].create(
|
||||||
'name': 'Test User',
|
{
|
||||||
'login': 'testuser',
|
"name": "Test User",
|
||||||
'caldav_calendar_url': 'http://test.calendar.url',
|
"login": "testuser",
|
||||||
'caldav_username': 'testuser',
|
"caldav_calendar_url": "http://test.calendar.url",
|
||||||
'caldav_password': 'password',
|
"caldav_username": "testuser",
|
||||||
})
|
"caldav_password": "password",
|
||||||
|
}
|
||||||
|
)
|
||||||
self.env = self.env(context=dict(self.env.context, no_reset_password=True))
|
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):
|
def test_create_caldav_event(self, mock_get_caldav_client):
|
||||||
mock_client = MagicMock()
|
mock_client = MagicMock()
|
||||||
mock_calendar = MagicMock()
|
mock_calendar = MagicMock()
|
||||||
|
|
@ -30,7 +34,7 @@ class TestCaldavSync(TransactionCase):
|
||||||
caldav_event = MagicMock()
|
caldav_event = MagicMock()
|
||||||
cal = Calendar.from_ical(ical_event)
|
cal = Calendar.from_ical(ical_event)
|
||||||
ical_event_instance = next(iter(cal.subcomponents))
|
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
|
return caldav_event
|
||||||
|
|
||||||
mock_calendar.add_event.side_effect = add_event_side_effect
|
mock_calendar.add_event.side_effect = add_event_side_effect
|
||||||
|
|
@ -38,56 +42,72 @@ class TestCaldavSync(TransactionCase):
|
||||||
mock_client.calendar.return_value = mock_calendar
|
mock_client.calendar.return_value = mock_calendar
|
||||||
|
|
||||||
event_data = {
|
event_data = {
|
||||||
'name': 'Test Event',
|
"name": "Test Event",
|
||||||
'start': '2024-05-22 10:00:00',
|
"start": "2024-05-22 10:00:00",
|
||||||
'stop': '2024-05-22 11:00:00',
|
"stop": "2024-05-22 11:00:00",
|
||||||
'description': '<p>This is a test event</p>',
|
"description": "This is a test event",
|
||||||
'location': 'Test Location',
|
"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])
|
cal = Calendar.from_ical(mock_calendar.add_event.call_args[0][0])
|
||||||
ical_event = next(iter(cal.subcomponents))
|
ical_event = next(iter(cal.subcomponents))
|
||||||
|
|
||||||
self.assertEqual(str(ical_event.get('summary')), event_data['name'])
|
self.assertEqual(str(ical_event.get("summary")), event_data["name"])
|
||||||
self.assertEqual(str(ical_event.get('location')), event_data['location'])
|
self.assertEqual(str(ical_event.get("location")), event_data["location"])
|
||||||
self.assertEqual(ical_event.get('description'), event_data['description'])
|
self.assertEqual(ical_event.get("description"), event_data["description"])
|
||||||
self.assertIsNotNone(event.caldav_uid)
|
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(
|
||||||
@patch('odoo.addons.caldav_sync.models.calendar_event.CalendarEvent.sync_update_to_caldav')
|
"odoo.addons.caldav_sync.models.calendar_event.CalendarEvent._get_caldav_client"
|
||||||
def test_update_caldav_event(self, mock_sync_update_to_caldav, mock_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_client = MagicMock()
|
||||||
mock_calendar = MagicMock()
|
mock_calendar = MagicMock()
|
||||||
mock_event = MagicMock()
|
mock_event = MagicMock()
|
||||||
mock_event.id = 'test-uid-12345'
|
mock_event.id = "test-uid-12345"
|
||||||
|
|
||||||
mock_client.calendar.return_value = mock_calendar
|
mock_client.calendar.return_value = mock_calendar
|
||||||
mock_calendar.add_event.return_value = mock_event
|
mock_calendar.add_event.return_value = mock_event
|
||||||
mock_get_caldav_client.return_value = mock_client
|
mock_get_caldav_client.return_value = mock_client
|
||||||
|
|
||||||
event = self.env['calendar.event'].with_user(self.user).create({
|
event = (
|
||||||
'name': 'Test Event',
|
self.env["calendar.event"]
|
||||||
'start': '2024-05-22 10:00:00',
|
.with_user(self.user)
|
||||||
'stop': '2024-05-22 11:00:00',
|
.create(
|
||||||
'description': 'This is a test event',
|
{
|
||||||
'location': 'Test Location',
|
"name": "Test Event",
|
||||||
'create_uid': self.user.id,
|
"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({
|
event.with_user(self.user).write(
|
||||||
'name': 'Updated Test Event',
|
{
|
||||||
'start': '2024-05-22 12:00:00',
|
"name": "Updated Test Event",
|
||||||
'stop': '2024-05-22 13:00:00',
|
"start": "2024-05-22 12:00:00",
|
||||||
})
|
"stop": "2024-05-22 13:00:00",
|
||||||
|
}
|
||||||
|
)
|
||||||
mock_sync_update_to_caldav.assert_called_once()
|
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))
|
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):
|
def test_delete_caldav_event(self, mock_get_caldav_client):
|
||||||
mock_client = MagicMock()
|
mock_client = MagicMock()
|
||||||
mock_calendar = MagicMock()
|
mock_calendar = MagicMock()
|
||||||
|
|
@ -97,24 +117,32 @@ class TestCaldavSync(TransactionCase):
|
||||||
mock_calendar.object_by_uid.return_value = mock_event
|
mock_calendar.object_by_uid.return_value = mock_event
|
||||||
mock_get_caldav_client.return_value = mock_client
|
mock_get_caldav_client.return_value = mock_client
|
||||||
|
|
||||||
event = self.env['calendar.event'].with_user(self.user).create({
|
event = (
|
||||||
'name': 'Test Event',
|
self.env["calendar.event"]
|
||||||
'start': '2024-05-22 10:00:00',
|
.with_user(self.user)
|
||||||
'stop': '2024-05-22 11:00:00',
|
.create(
|
||||||
'description': 'This is a test event',
|
{
|
||||||
'location': 'Test Location',
|
"name": "Test Event",
|
||||||
'create_uid': self.user.id,
|
"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
|
uid = event.caldav_uid
|
||||||
event.with_user(self.user).unlink()
|
event.with_user(self.user).unlink()
|
||||||
|
|
||||||
mock_calendar.object_by_uid.assert_called_once_with(uid)
|
mock_calendar.object_by_uid.assert_called_once_with(uid)
|
||||||
mock_event.delete.assert_called_once()
|
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):
|
def test_poll_caldav_server(self, mock_sync_event_from_ical):
|
||||||
mock_sync_event_from_ical.return_value = None
|
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_client = MockClient.return_value
|
||||||
mock_calendar = mock_client.calendar.return_value
|
mock_calendar = mock_client.calendar.return_value
|
||||||
mock_event = MagicMock()
|
mock_event = MagicMock()
|
||||||
|
|
@ -122,27 +150,18 @@ class TestCaldavSync(TransactionCase):
|
||||||
# Create a Calendar object and add an Event to it
|
# Create a Calendar object and add an Event to it
|
||||||
cal = Calendar()
|
cal = Calendar()
|
||||||
event = Event()
|
event = Event()
|
||||||
event.add('uid', 'test-uid-12345')
|
event.add("uid", "test-uid-12345")
|
||||||
event.add('dtstamp', datetime(2024, 5, 22, 10, 0, 0))
|
event.add("dtstamp", datetime(2024, 5, 22, 10, 0, 0))
|
||||||
event.add('dtstart', 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("dtend", datetime(2024, 5, 22, 11, 0, 0))
|
||||||
event.add('summary', 'Polled Event')
|
event.add("summary", "Polled Event")
|
||||||
event.add('description', 'This event was polled from CalDAV')
|
event.add("description", "This event was polled from CalDAV")
|
||||||
event.add('location', 'Polled Location')
|
event.add("location", "Polled Location")
|
||||||
cal.add_component(event)
|
cal.add_component(event)
|
||||||
|
|
||||||
# Set the mock event's icalendar_instance to the iCal string
|
# Set the mock event's icalendar_instance to the iCal string
|
||||||
mock_event.icalendar_instance = Calendar.from_ical(cal.to_ical())
|
mock_event.icalendar_instance = Calendar.from_ical(cal.to_ical())
|
||||||
mock_calendar.events.return_value = [mock_event]
|
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()
|
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')
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue