caldav: add SEQUENCE field when missing on event update (#88417)
gitea/passerelle/pipeline/head This commit looks good Details

This commit is contained in:
Yann Weber 2024-03-20 18:16:35 +01:00
parent 4a243f72e5
commit 923125e786
2 changed files with 66 additions and 11 deletions

View File

@ -162,6 +162,7 @@ class CalDAV(BaseResource):
cal = self.get_calendar(username)
self._process_event_properties(post_data)
post_data['SEQUENCE'] = 0 # RFC 5545 3.8.7.4
try:
evt = cal.save_event(**post_data)
except requests.exceptions.RequestException as expt:
@ -207,6 +208,10 @@ class CalDAV(BaseResource):
for k, v in post_data.items():
vevent.pop(k)
vevent.add(k, v)
if 'SEQUENCE' not in vevent:
# SEQUENCE is auto-incremented when present
# here after a 1st modification the SEQUENCE will be 1 (not 0)
vevent['SEQUENCE'] = 0
try:
# do not use ical.save(no_create=True) : no_create fails on some calDAV
ical.save()

View File

@ -260,6 +260,7 @@ def test_caldav_event_create_allday_ok(app, caldav_conn):
k: v if k not in ('DTSTART', 'DTEND') else datetime.date.fromisoformat(v)
for k, v in event.items()
}
event_conv['SEQUENCE'] = 0
cal_mock.save_event.assert_called_once_with(**event_conv)
@ -284,6 +285,7 @@ def test_caldav_event_categories(app, caldav_conn):
for k, v in event.items()
}
event_conv['CATEGORIES'] = [event_conv.pop('CATEGORY')]
event_conv['SEQUENCE'] = 0
cal_mock.save_event.assert_called_once_with(**event_conv)
@ -308,6 +310,7 @@ def test_caldav_event_create_ok(app, caldav_conn, event):
save_args = event.copy()
caldav_conn._process_event_properties(save_args)
save_args['SEQUENCE'] = 0
cal_mock.save_event.assert_called_once_with(**save_args)
@ -360,7 +363,7 @@ def test_caldav_event_create_bad_dates(app, caldav_conn, dtstart, dtend, until):
'DTSTART': dtstart,
'DTEND': dtend,
'SUMMARY': 'hello',
'RRULE': {'FREQ': 'WEEKLY', 'BYDAY': 'FR', 'UNTIL': until},
'RRULE': {'FREQ': 'WEEKLY', 'BYDAY': ['FR'], 'UNTIL': until},
}
qs_params = {'username': username}
@ -368,6 +371,7 @@ def test_caldav_event_create_bad_dates(app, caldav_conn, dtstart, dtend, until):
assert resp.status_code == 400
assert resp.json['err'] != 0
assert resp.json['err_desc'].startswith('Unable to convert field')
@pytest.mark.parametrize('transp', [True, False])
@ -420,8 +424,17 @@ def test_caldav_event_update_ok(app, transp, caldav_conn):
assert str(evt[k]) == v
@pytest.mark.parametrize(
'event_update',
(
{'DTSTART': '2020-02-20', 'DTEND': '2020-03-30', 'SUMMARY': 'Foobar'},
{'TRANSP': True},
{'RRULE/FREQ': 'MONTHLY', 'RRULE/BYDAY': ['TU'], 'RRULE/UNTIL': '2020-10-01'},
{'DTSTART': '2020-02-20', 'TRANSP': True},
),
)
@responses.activate
def test_caldav_event_update_ok_nomock(app, caldav_conn):
def test_caldav_event_update_ok_nomock(app, caldav_conn, event_update):
"""This test let the caldav lib think it is able to retrieve
an event in order to modify it.
This can trigger errors from icalendar lib when caldav
@ -432,22 +445,59 @@ def test_caldav_event_update_ok_nomock(app, caldav_conn):
evt_id = '01234567-abcd'
evt_url = DAV_URL + get_event_path(caldav_conn, username, evt_id)
event = {
'DTSTART': '2020-02-20',
'DTEND': '2020-03-30',
'SUMMARY': 'foobar',
'TRANSP': True,
'RRULE': {'FREQ': 'MONTHLY', 'BYDAY': ['FR'], 'UNTIL': '2020-10-01'},
}
qs_params = {'username': username, 'event_id': evt_id}
responses.add(responses.GET, evt_url, body=FAKE_ICS)
responses.add(responses.PUT, evt_url, status=201)
responses.add(responses.PUT, evt_url, status=204, body='')
resp = app_post(app, url, qs_params, event)
resp = app_post(app, url, qs_params, event_update)
assert resp.json['err'] == 0
assert resp.json['data']['event_id'] == evt_id
assert len(responses.calls) == 2
assert responses.calls[1].request.method == 'PUT'
@responses.activate
def test_caldav_event_update_sequence_ok(app, caldav_conn):
url = tests.utils.generic_endpoint_url('caldav', 'event/update')
username = 'foobar'
evt_id = '1234567890'
evt_url = DAV_URL + get_event_path(caldav_conn, username, evt_id)
qs_params = {'username': username, 'event_id': evt_id}
responses.add(responses.GET, evt_url, body=FAKE_ICS)
responses.add(responses.PUT, evt_url, status=204, body='')
# testing SEQUENCE initialization : if missing we add it
dtend = datetime.date.fromisoformat('2024-02-29')
resp = app_post(app, url, qs_params, {'DTEND': dtend.isoformat()})
assert resp.json['err'] == 0
assert len(responses.calls) == 2
put_mock = responses.calls[1]
assert put_mock.request.method == 'PUT'
raw_ics = put_mock.request.body
calendar = icalendar.Calendar.from_ical(raw_ics)
vevent = calendar.walk('VEVENT')[0]
assert vevent['SEQUENCE'] == 1
for expt_sequence in range(2, 10):
# testing SEQUENCE incrementation on modification
responses.reset()
responses.add(responses.GET, evt_url, body=raw_ics)
responses.add(responses.PUT, evt_url, status=204, body='')
dtend += datetime.timedelta(days=1)
resp = app_post(app, url, qs_params, {'DTEND': dtend.isoformat()})
assert resp.json['err'] == 0
assert len(responses.calls) == 2
assert responses.calls[1].request.method == 'PUT'
raw_ics = responses.calls[1].request.body
calendar = icalendar.Calendar.from_ical(raw_ics)
vevent = calendar.walk('VEVENT')[0]
assert vevent['SEQUENCE'] == expt_sequence
@responses.activate
def test_caldav_event_update_notfound(app, caldav_conn):