diff --git a/.sqlx/query-3a29efff3d3f6e1e05595d1a2d095af5fc963572c90bd10a6616af78757f8c39.json b/.sqlx/query-3a29efff3d3f6e1e05595d1a2d095af5fc963572c90bd10a6616af78757f8c39.json new file mode 100644 index 0000000..f343cd0 --- /dev/null +++ b/.sqlx/query-3a29efff3d3f6e1e05595d1a2d095af5fc963572c90bd10a6616af78757f8c39.json @@ -0,0 +1,32 @@ +{ + "db_name": "SQLite", + "query": "SELECT id, uid, ics FROM calendarobjects\n WHERE principal = ? AND cal_id = ? AND deleted_at IS NULL\n AND (last_occurence IS NULL OR ? IS NULL OR last_occurence >= date(?))\n AND (first_occurence IS NULL OR ? IS NULL OR first_occurence <= date(?))\n ", + "describe": { + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "uid", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "ics", + "ordinal": 2, + "type_info": "Text" + } + ], + "parameters": { + "Right": 6 + }, + "nullable": [ + false, + false, + false + ] + }, + "hash": "3a29efff3d3f6e1e05595d1a2d095af5fc963572c90bd10a6616af78757f8c39" +} diff --git a/.sqlx/query-3e1cca532372e891ab3e604ecb79311d8cd64108d4f238db4c79e9467a3b6d2e.json b/.sqlx/query-3e1cca532372e891ab3e604ecb79311d8cd64108d4f238db4c79e9467a3b6d2e.json deleted file mode 100644 index cd4424e..0000000 --- a/.sqlx/query-3e1cca532372e891ab3e604ecb79311d8cd64108d4f238db4c79e9467a3b6d2e.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "db_name": "SQLite", - "query": "REPLACE INTO calendarobjects (principal, cal_id, id, ics, first_occurence, last_occurence, etag, object_type) VALUES (?, ?, ?, ?, date(?), date(?), ?, ?)", - "describe": { - "columns": [], - "parameters": { - "Right": 8 - }, - "nullable": [] - }, - "hash": "3e1cca532372e891ab3e604ecb79311d8cd64108d4f238db4c79e9467a3b6d2e" -} diff --git a/.sqlx/query-543838c030550cb09d1af08adfeade8b7ce3575d92fddbc6e9582d141bc9e49d.json b/.sqlx/query-505ebe8e64ac709b230dce7150240965e45442aca6c5f3b3115738ef508939ed.json similarity index 52% rename from .sqlx/query-543838c030550cb09d1af08adfeade8b7ce3575d92fddbc6e9582d141bc9e49d.json rename to .sqlx/query-505ebe8e64ac709b230dce7150240965e45442aca6c5f3b3115738ef508939ed.json index 91945b2..73658e2 100644 --- a/.sqlx/query-543838c030550cb09d1af08adfeade8b7ce3575d92fddbc6e9582d141bc9e49d.json +++ b/.sqlx/query-505ebe8e64ac709b230dce7150240965e45442aca6c5f3b3115738ef508939ed.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "SELECT id, ics FROM calendarobjects WHERE (principal, cal_id, id) = (?, ?, ?) AND ((deleted_at IS NULL) OR ?)", + "query": "SELECT id, uid, ics FROM calendarobjects WHERE (principal, cal_id, id) = (?, ?, ?) AND ((deleted_at IS NULL) OR ?)", "describe": { "columns": [ { @@ -9,18 +9,24 @@ "type_info": "Text" }, { - "name": "ics", + "name": "uid", "ordinal": 1, "type_info": "Text" + }, + { + "name": "ics", + "ordinal": 2, + "type_info": "Text" } ], "parameters": { "Right": 4 }, "nullable": [ + false, false, false ] }, - "hash": "543838c030550cb09d1af08adfeade8b7ce3575d92fddbc6e9582d141bc9e49d" + "hash": "505ebe8e64ac709b230dce7150240965e45442aca6c5f3b3115738ef508939ed" } diff --git a/.sqlx/query-6327bee90e5df01536a0ddb15adcc37af3027f6902aa3786365c5ab2fbf06bda.json b/.sqlx/query-6327bee90e5df01536a0ddb15adcc37af3027f6902aa3786365c5ab2fbf06bda.json deleted file mode 100644 index 185ae45..0000000 --- a/.sqlx/query-6327bee90e5df01536a0ddb15adcc37af3027f6902aa3786365c5ab2fbf06bda.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "db_name": "SQLite", - "query": "INSERT INTO calendarobjects (principal, cal_id, id, ics, first_occurence, last_occurence, etag, object_type) VALUES (?, ?, ?, ?, date(?), date(?), ?, ?)", - "describe": { - "columns": [], - "parameters": { - "Right": 8 - }, - "nullable": [] - }, - "hash": "6327bee90e5df01536a0ddb15adcc37af3027f6902aa3786365c5ab2fbf06bda" -} diff --git a/.sqlx/query-54c9c0e36a52e6963f11c6aa27f13aafb4204b8aa34b664fd825bd447db80e86.json b/.sqlx/query-804ed2a4a7032e9605d1871297498f5a96de0fc816ce660c705fb28318be0d42.json similarity index 53% rename from .sqlx/query-54c9c0e36a52e6963f11c6aa27f13aafb4204b8aa34b664fd825bd447db80e86.json rename to .sqlx/query-804ed2a4a7032e9605d1871297498f5a96de0fc816ce660c705fb28318be0d42.json index bf83a1f..c67abd7 100644 --- a/.sqlx/query-54c9c0e36a52e6963f11c6aa27f13aafb4204b8aa34b664fd825bd447db80e86.json +++ b/.sqlx/query-804ed2a4a7032e9605d1871297498f5a96de0fc816ce660c705fb28318be0d42.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "SELECT id, ics FROM calendarobjects WHERE principal = ? AND cal_id = ? AND deleted_at IS NULL", + "query": "SELECT id, uid, ics FROM calendarobjects WHERE principal = ? AND cal_id = ? AND deleted_at IS NULL", "describe": { "columns": [ { @@ -9,18 +9,24 @@ "type_info": "Text" }, { - "name": "ics", + "name": "uid", "ordinal": 1, "type_info": "Text" + }, + { + "name": "ics", + "ordinal": 2, + "type_info": "Text" } ], "parameters": { "Right": 2 }, "nullable": [ + false, false, false ] }, - "hash": "54c9c0e36a52e6963f11c6aa27f13aafb4204b8aa34b664fd825bd447db80e86" + "hash": "804ed2a4a7032e9605d1871297498f5a96de0fc816ce660c705fb28318be0d42" } diff --git a/.sqlx/query-a68a1b96189b854a7ba2a3cd866ba583af5ad84bc1cd8b20cb805e9ce3bad820.json b/.sqlx/query-a68a1b96189b854a7ba2a3cd866ba583af5ad84bc1cd8b20cb805e9ce3bad820.json new file mode 100644 index 0000000..2eee142 --- /dev/null +++ b/.sqlx/query-a68a1b96189b854a7ba2a3cd866ba583af5ad84bc1cd8b20cb805e9ce3bad820.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "REPLACE INTO calendarobjects (principal, cal_id, id, uid, ics, first_occurence, last_occurence, etag, object_type) VALUES (?, ?, ?, ?, ?, date(?), date(?), ?, ?)", + "describe": { + "columns": [], + "parameters": { + "Right": 9 + }, + "nullable": [] + }, + "hash": "a68a1b96189b854a7ba2a3cd866ba583af5ad84bc1cd8b20cb805e9ce3bad820" +} diff --git a/.sqlx/query-c550dbf3d5ce7069f28d767ea9045e477ef8d29d6186851760757a06dec42339.json b/.sqlx/query-c550dbf3d5ce7069f28d767ea9045e477ef8d29d6186851760757a06dec42339.json deleted file mode 100644 index 2c5ddda..0000000 --- a/.sqlx/query-c550dbf3d5ce7069f28d767ea9045e477ef8d29d6186851760757a06dec42339.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "db_name": "SQLite", - "query": "SELECT id, ics FROM calendarobjects\n WHERE principal = ? AND cal_id = ? AND deleted_at IS NULL\n AND (last_occurence IS NULL OR ? IS NULL OR last_occurence >= date(?))\n AND (first_occurence IS NULL OR ? IS NULL OR first_occurence <= date(?))\n ", - "describe": { - "columns": [ - { - "name": "id", - "ordinal": 0, - "type_info": "Text" - }, - { - "name": "ics", - "ordinal": 1, - "type_info": "Text" - } - ], - "parameters": { - "Right": 6 - }, - "nullable": [ - false, - false - ] - }, - "hash": "c550dbf3d5ce7069f28d767ea9045e477ef8d29d6186851760757a06dec42339" -} diff --git a/.sqlx/query-d498a758ed707408b00b7d2675250ea739a681ce1f009f05e97f2e101bd7e556.json b/.sqlx/query-d498a758ed707408b00b7d2675250ea739a681ce1f009f05e97f2e101bd7e556.json new file mode 100644 index 0000000..83d624f --- /dev/null +++ b/.sqlx/query-d498a758ed707408b00b7d2675250ea739a681ce1f009f05e97f2e101bd7e556.json @@ -0,0 +1,12 @@ +{ + "db_name": "SQLite", + "query": "INSERT INTO calendarobjects (principal, cal_id, id, uid, ics, first_occurence, last_occurence, etag, object_type) VALUES (?, ?, ?, ?, ?, date(?), date(?), ?, ?)", + "describe": { + "columns": [], + "parameters": { + "Right": 9 + }, + "nullable": [] + }, + "hash": "d498a758ed707408b00b7d2675250ea739a681ce1f009f05e97f2e101bd7e556" +} diff --git a/crates/caldav/src/calendar/methods/import.rs b/crates/caldav/src/calendar/methods/import.rs index 4a82cb2..deb3b05 100644 --- a/crates/caldav/src/calendar/methods/import.rs +++ b/crates/caldav/src/calendar/methods/import.rs @@ -82,7 +82,7 @@ pub async fn route_import( let objects = expanded_cals .into_iter() .map(|cal| cal.generate()) - .map(CalendarObject::from_ics) + .map(|ics| CalendarObject::from_ics(ics, None)) .collect::, _>>()?; let new_cal = Calendar { principal, diff --git a/crates/caldav/src/calendar/methods/report/mod.rs b/crates/caldav/src/calendar/methods/report/mod.rs index 75984e5..2703f01 100644 --- a/crates/caldav/src/calendar/methods/report/mod.rs +++ b/crates/caldav/src/calendar/methods/report/mod.rs @@ -61,7 +61,7 @@ fn objects_response( ) -> Result, Error> { let mut responses = Vec::new(); for object in objects { - let path = format!("{}/{}.ics", path, object.get_uid()); + let path = format!("{}/{}.ics", path, object.get_id()); responses.push( CalendarObjectResource { object, diff --git a/crates/caldav/src/calendar/methods/report/sync_collection.rs b/crates/caldav/src/calendar/methods/report/sync_collection.rs index bb11e78..7b70f20 100644 --- a/crates/caldav/src/calendar/methods/report/sync_collection.rs +++ b/crates/caldav/src/calendar/methods/report/sync_collection.rs @@ -33,7 +33,7 @@ pub async fn handle_sync_collection( let mut responses = Vec::new(); for object in new_objects { - let path = format!("{}/{}.ics", path, object.get_uid()); + let path = format!("{}/{}.ics", path, object.get_id()); responses.push( CalendarObjectResource { object, diff --git a/crates/caldav/src/calendar_object/methods.rs b/crates/caldav/src/calendar_object/methods.rs index 177a730..4e3eb32 100644 --- a/crates/caldav/src/calendar_object/methods.rs +++ b/crates/caldav/src/calendar_object/methods.rs @@ -11,7 +11,7 @@ use rustical_ical::CalendarObject; use rustical_store::CalendarStore; use rustical_store::auth::Principal; use std::str::FromStr; -use tracing::{debug, error, instrument}; +use tracing::{debug, instrument}; #[instrument(skip(cal_store))] pub async fn get_event( @@ -78,18 +78,10 @@ pub async fn put_event( true }; - let Ok(object) = CalendarObject::from_ics(body.clone()) else { + let Ok(object) = CalendarObject::from_ics(body.clone(), Some(object_id)) else { debug!("invalid calendar data:\n{body}"); return Err(Error::PreconditionFailed(Precondition::ValidCalendarData)); }; - if object.get_uid() != object_id { - error!( - "Calendar object UID and file name not matching: UID={}, filename={}", - object.get_uid(), - object_id - ); - return Err(Error::PreconditionFailed(Precondition::MatchingUid)); - } cal_store .put_object(principal, calendar_id, object, overwrite) .await?; diff --git a/crates/caldav/src/calendar_object/resource.rs b/crates/caldav/src/calendar_object/resource.rs index bb54344..15af050 100644 --- a/crates/caldav/src/calendar_object/resource.rs +++ b/crates/caldav/src/calendar_object/resource.rs @@ -21,7 +21,7 @@ pub struct CalendarObjectResource { impl ResourceName for CalendarObjectResource { fn get_name(&self) -> String { - format!("{}.ics", self.object.get_uid()) + format!("{}.ics", self.object.get_id()) } } diff --git a/crates/caldav/src/error.rs b/crates/caldav/src/error.rs index 790646f..833db6c 100644 --- a/crates/caldav/src/error.rs +++ b/crates/caldav/src/error.rs @@ -12,8 +12,6 @@ pub enum Precondition { #[error("valid-calendar-data")] #[xml(ns = "rustical_dav::namespace::NS_CALDAV")] ValidCalendarData, - #[error("matching-uid")] - MatchingUid, } impl IntoResponse for Precondition { diff --git a/crates/ical/src/address_object.rs b/crates/ical/src/address_object.rs index 40f5082..09f73d9 100644 --- a/crates/ical/src/address_object.rs +++ b/crates/ical/src/address_object.rs @@ -97,8 +97,9 @@ impl AddressObject { let uid = format!("{}-anniversary", self.get_id()); let year_suffix = year.map(|year| format!(" ({year})")).unwrap_or_default(); - Some(CalendarObject::from_ics(format!( - r"BEGIN:VCALENDAR + Some(CalendarObject::from_ics( + format!( + r"BEGIN:VCALENDAR VERSION:2.0 CALSCALE:GREGORIAN PRODID:-//github.com/lennart-k/rustical birthday calendar//EN @@ -116,7 +117,9 @@ DESCRIPTION:💍 {fullname}{year_suffix} END:VALARM END:VEVENT END:VCALENDAR", - ))?) + ), + None, + )?) } else { None }, @@ -136,8 +139,9 @@ END:VCALENDAR", let uid = format!("{}-birthday", self.get_id()); let year_suffix = year.map(|year| format!(" ({year})")).unwrap_or_default(); - Some(CalendarObject::from_ics(format!( - r"BEGIN:VCALENDAR + Some(CalendarObject::from_ics( + format!( + r"BEGIN:VCALENDAR VERSION:2.0 CALSCALE:GREGORIAN PRODID:-//github.com/lennart-k/rustical birthday calendar//EN @@ -155,7 +159,9 @@ DESCRIPTION:🎂 {fullname}{year_suffix} END:VALARM END:VEVENT END:VCALENDAR", - ))?) + ), + None, + )?) } else { None }, diff --git a/crates/ical/src/icalendar/event.rs b/crates/ical/src/icalendar/event.rs index 4ed18cb..08acfbe 100644 --- a/crates/ical/src/icalendar/event.rs +++ b/crates/ical/src/icalendar/event.rs @@ -251,7 +251,7 @@ END:VEVENT\r\n", #[test] fn test_expand_recurrence() { - let event = CalendarObject::from_ics(ICS.to_string()).unwrap(); + let event = CalendarObject::from_ics(ICS.to_string(), None).unwrap(); let crate::CalendarObjectComponent::Event(event, overrides) = event.get_data() else { panic!() }; diff --git a/crates/ical/src/icalendar/object.rs b/crates/ical/src/icalendar/object.rs index ce3dbe7..d31a0a0 100644 --- a/crates/ical/src/icalendar/object.rs +++ b/crates/ical/src/icalendar/object.rs @@ -64,6 +64,19 @@ pub enum CalendarObjectComponent { Journal(IcalJournal, Vec), } +impl CalendarObjectComponent { + #[must_use] + pub fn get_uid(&self) -> &str { + match &self { + // We've made sure before that the first component exists and all components share the + // same UID + Self::Todo(todo, _) => todo.get_uid(), + Self::Event(event, _) => event.event.get_uid(), + Self::Journal(journal, _) => journal.get_uid(), + } + } +} + impl From<&CalendarObjectComponent> for CalendarObjectType { fn from(value: &CalendarObjectComponent) -> Self { match value { @@ -141,12 +154,13 @@ impl CalendarObjectComponent { pub struct CalendarObject { data: CalendarObjectComponent, properties: Vec, + id: String, ics: String, vtimezones: HashMap, } impl CalendarObject { - pub fn from_ics(ics: String) -> Result { + pub fn from_ics(ics: String, id: Option) -> Result { let mut parser = ical::IcalParser::new(BufReader::new(ics.as_bytes())); let cal = parser.next().ok_or(Error::MissingCalendar)??; if parser.next().is_some() { @@ -202,6 +216,7 @@ impl CalendarObject { }; Ok(Self { + id: id.unwrap_or_else(|| data.get_uid().to_owned()), data, properties: cal.properties, ics, @@ -221,13 +236,12 @@ impl CalendarObject { #[must_use] pub fn get_uid(&self) -> &str { - match &self.data { - // We've made sure before that the first component exists and all components share the - // same UID - CalendarObjectComponent::Todo(todo, _) => todo.get_uid(), - CalendarObjectComponent::Event(event, _) => event.event.get_uid(), - CalendarObjectComponent::Journal(journal, _) => journal.get_uid(), - } + self.data.get_uid() + } + + #[must_use] + pub fn get_id(&self) -> &str { + &self.id } #[must_use] diff --git a/crates/ical/tests/test_cal_object.rs b/crates/ical/tests/test_cal_object.rs index 71db7ab..9fc4ffb 100644 --- a/crates/ical/tests/test_cal_object.rs +++ b/crates/ical/tests/test_cal_object.rs @@ -25,6 +25,6 @@ END:VCALENDAR #[test] fn parse_calendar_object() { - let object = CalendarObject::from_ics(MULTI_VEVENT.to_string()).unwrap(); + let object = CalendarObject::from_ics(MULTI_VEVENT.to_string(), None).unwrap(); object.expand_recurrence(None, None).unwrap(); } diff --git a/crates/store_sqlite/migrations/20251101181540_add_calendar_uid.down.sql b/crates/store_sqlite/migrations/20251101181540_add_calendar_uid.down.sql new file mode 100644 index 0000000..ca4e628 --- /dev/null +++ b/crates/store_sqlite/migrations/20251101181540_add_calendar_uid.down.sql @@ -0,0 +1,47 @@ +DROP INDEX idx_calobjs_uid; +ALTER TABLE calendarobjects RENAME TO calendarobjects_old; + +CREATE TABLE calendarobjects ( + principal TEXT NOT NULL, + cal_id TEXT NOT NULL, + id TEXT NOT NULL, -- filename + ics TEXT NOT NULL, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, + deleted_at DATETIME, + + -- For more efficient calendar-queries + first_occurence DATE, + last_occurence DATE, + etag TEXT, + object_type INTEGER NOT NULL, -- VEVENT(0)/VTODO(1)/VJOURNAL(2) + + CONSTRAINT pk_calendarobject_id PRIMARY KEY (principal, cal_id, id), + CONSTRAINT fk_calendarobject_calendar FOREIGN KEY (principal, cal_id) + REFERENCES calendars (principal, id) ON DELETE CASCADE +); + +INSERT INTO calendarobjects ( + principal, + cal_id, + id, + ics, + updated_at, + deleted_at, + first_occurence, + last_occurence, + etag, + object_type +) SELECT + principal, + cal_id, + id, + ics, + updated_at, + deleted_at, + first_occurence, + last_occurence, + etag, + object_type +FROM calendarobjects_old; + +DROP TABLE calendarobjects_old; diff --git a/crates/store_sqlite/migrations/20251101181540_add_calendar_uid.up.sql b/crates/store_sqlite/migrations/20251101181540_add_calendar_uid.up.sql new file mode 100644 index 0000000..1006424 --- /dev/null +++ b/crates/store_sqlite/migrations/20251101181540_add_calendar_uid.up.sql @@ -0,0 +1,53 @@ +-- Adds the column "uid" and populates it with data from "id" +ALTER TABLE calendarobjects RENAME TO calendarobjects_old; + +CREATE TABLE calendarobjects ( + principal TEXT NOT NULL, + cal_id TEXT NOT NULL, + id TEXT NOT NULL, -- filename + "uid" TEXT NOT NULL, -- global identifier + ics TEXT NOT NULL, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, + deleted_at DATETIME, + + -- For more efficient calendar-queries + first_occurence DATE, + last_occurence DATE, + etag TEXT, + object_type INTEGER NOT NULL, -- VEVENT(0)/VTODO(1)/VJOURNAL(2) + + CONSTRAINT pk_calendarobject_id PRIMARY KEY (principal, cal_id, id), + CONSTRAINT uq_calendarobject_uid UNIQUE (principal, cal_id, "uid"), + CONSTRAINT fk_calendarobject_calendar FOREIGN KEY (principal, cal_id) + REFERENCES calendars (principal, id) ON DELETE CASCADE +); + +CREATE INDEX idx_calobjs_uid ON calendarobjects (principal, cal_id, "uid"); + +INSERT INTO calendarobjects ( + principal, + cal_id, + id, + "uid", + ics, + updated_at, + deleted_at, + first_occurence, + last_occurence, + etag, + object_type +) SELECT + principal, + cal_id, + id, + id AS "uid", + ics, + updated_at, + deleted_at, + first_occurence, + last_occurence, + etag, + object_type +FROM calendarobjects_old; + +DROP TABLE calendarobjects_old; diff --git a/crates/store_sqlite/src/calendar_store.rs b/crates/store_sqlite/src/calendar_store.rs index a24c050..5c47d84 100644 --- a/crates/store_sqlite/src/calendar_store.rs +++ b/crates/store_sqlite/src/calendar_store.rs @@ -17,19 +17,20 @@ use tracing::{error, instrument}; struct CalendarObjectRow { id: String, ics: String, + uid: String, } impl TryFrom for CalendarObject { type Error = rustical_store::Error; fn try_from(value: CalendarObjectRow) -> Result { - let object = Self::from_ics(value.ics)?; - if object.get_uid() != value.id { + let object = Self::from_ics(value.ics, Some(value.id))?; + if object.get_uid() != value.uid { return Err(rustical_store::Error::IcalError( rustical_ical::Error::InvalidData(format!( - "object_id={} and UID={} don't match", - object.get_uid(), - value.id + "uid={} and UID={} don't match", + value.uid, + object.get_uid() )), )); } @@ -281,7 +282,7 @@ impl SqliteCalendarStore { ) -> Result, Error> { sqlx::query_as!( CalendarObjectRow, - "SELECT id, ics FROM calendarobjects WHERE principal = ? AND cal_id = ? AND deleted_at IS NULL", + "SELECT id, uid, ics FROM calendarobjects WHERE principal = ? AND cal_id = ? AND deleted_at IS NULL", principal, cal_id ) @@ -306,7 +307,7 @@ impl SqliteCalendarStore { sqlx::query_as!( CalendarObjectRow, - r"SELECT id, ics FROM calendarobjects + r"SELECT id, uid, ics FROM calendarobjects WHERE principal = ? AND cal_id = ? AND deleted_at IS NULL AND (last_occurence IS NULL OR ? IS NULL OR last_occurence >= date(?)) AND (first_occurence IS NULL OR ? IS NULL OR first_occurence <= date(?)) @@ -335,7 +336,7 @@ impl SqliteCalendarStore { ) -> Result { sqlx::query_as!( CalendarObjectRow, - "SELECT id, ics FROM calendarobjects WHERE (principal, cal_id, id) = (?, ?, ?) AND ((deleted_at IS NULL) OR ?)", + "SELECT id, uid, ics FROM calendarobjects WHERE (principal, cal_id, id) = (?, ?, ?) AND ((deleted_at IS NULL) OR ?)", principal, cal_id, object_id, @@ -355,7 +356,7 @@ impl SqliteCalendarStore { object: CalendarObject, overwrite: bool, ) -> Result<(), Error> { - let (object_id, ics) = (object.get_uid(), object.get_ics()); + let (object_id, uid, ics) = (object.get_id(), object.get_uid(), object.get_ics()); let first_occurence = object .get_first_occurence() @@ -374,10 +375,11 @@ impl SqliteCalendarStore { (if overwrite { sqlx::query!( - "REPLACE INTO calendarobjects (principal, cal_id, id, ics, first_occurence, last_occurence, etag, object_type) VALUES (?, ?, ?, ?, date(?), date(?), ?, ?)", + "REPLACE INTO calendarobjects (principal, cal_id, id, uid, ics, first_occurence, last_occurence, etag, object_type) VALUES (?, ?, ?, ?, ?, date(?), date(?), ?, ?)", principal, cal_id, object_id, + uid, ics, first_occurence, last_occurence, @@ -387,10 +389,11 @@ impl SqliteCalendarStore { } else { // If the object already exists a database error is thrown and handled in error.rs sqlx::query!( - "INSERT INTO calendarobjects (principal, cal_id, id, ics, first_occurence, last_occurence, etag, object_type) VALUES (?, ?, ?, ?, date(?), date(?), ?, ?)", + "INSERT INTO calendarobjects (principal, cal_id, id, uid, ics, first_occurence, last_occurence, etag, object_type) VALUES (?, ?, ?, ?, ?, date(?), date(?), ?, ?)", principal, cal_id, object_id, + uid, ics, first_occurence, last_occurence, @@ -410,7 +413,7 @@ impl SqliteCalendarStore { executor: E, principal: &str, cal_id: &str, - id: &str, + object_id: &str, use_trashbin: bool, ) -> Result<(), Error> { if use_trashbin { @@ -418,7 +421,7 @@ impl SqliteCalendarStore { "UPDATE calendarobjects SET deleted_at = datetime(), updated_at = datetime() WHERE (principal, cal_id, id) = (?, ?, ?)", principal, cal_id, - id + object_id ) .execute(executor) .await.map_err(crate::Error::from)?; @@ -426,7 +429,7 @@ impl SqliteCalendarStore { sqlx::query!( "DELETE FROM calendarobjects WHERE cal_id = ? AND id = ?", cal_id, - id + object_id ) .execute(executor) .await @@ -678,7 +681,7 @@ impl CalendarStore for SqliteCalendarStore { .await .map_err(crate::Error::from)?; - let object_id = object.get_uid().to_owned(); + let object_id = object.get_id().to_owned(); let calendar = Self::_get_calendar(&mut *tx, &principal, &cal_id, true).await?; if calendar.subscription_url.is_some() {