JMAP for Calendars implementation (part 1)

This commit is contained in:
mdecimus
2025-10-05 16:06:57 +02:00
parent c47413a42b
commit b7df05dd3b
94 changed files with 5352 additions and 543 deletions

View File

@@ -183,7 +183,7 @@ pub(super) async fn build_scheduling_resources(
.core
.storage
.data
.get_last_change_id(account_id, SyncCollection::CalendarScheduling.into())
.get_last_change_id(account_id, SyncCollection::CalendarEventNotification.into())
.await
.caused_by(trc::location!())?
.unwrap_or_default();
@@ -196,7 +196,7 @@ pub(super) async fn build_scheduling_resources(
.unwrap_or_else(|| format!("_{account_id}"));
let item_ids = server
.get_document_ids(account_id, Collection::CalendarScheduling)
.get_document_ids(account_id, Collection::CalendarEventNotification)
.await
.caused_by(trc::location!())?
.unwrap_or_default();
@@ -326,7 +326,7 @@ pub(super) fn resource_from_event(event: &ArchivedCalendarEvent, document_id: u3
pub(super) fn resource_from_scheduling(document_id: u32, is_container: bool) -> DavResource {
DavResource {
document_id,
data: DavResourceMetadata::CalendarScheduling {
data: DavResourceMetadata::CalendarEventNotification {
names: if !is_container {
[DavName {
name: format!("{document_id}.ics"),

View File

@@ -77,7 +77,7 @@ impl GroupwareCache for Server {
SyncCollection::Calendar => &self.inner.cache.events,
SyncCollection::AddressBook => &self.inner.cache.contacts,
SyncCollection::FileNode => &self.inner.cache.files,
SyncCollection::CalendarScheduling => &self.inner.cache.scheduling,
SyncCollection::CalendarEventNotification => &self.inner.cache.scheduling,
_ => unreachable!(),
};
let cache_ = match cache_store.get_value_or_guard_async(&account_id).await {
@@ -178,7 +178,7 @@ impl GroupwareCache for Server {
}
let num_changes = changes.changes.len();
let cache = if !matches!(collection, SyncCollection::CalendarScheduling) {
let cache = if !matches!(collection, SyncCollection::CalendarEventNotification) {
let mut updated_resources = AHashMap::with_capacity(8);
let has_no_children = collection == SyncCollection::FileNode;
@@ -517,7 +517,7 @@ async fn full_cache_build(
.await
}
SyncCollection::FileNode => build_file_resources(server, account_id, update_lock).await,
SyncCollection::CalendarScheduling => {
SyncCollection::CalendarEventNotification => {
build_scheduling_resources(server, account_id, update_lock).await
}
_ => unreachable!(),

View File

@@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use crate::calendar::{ArchivedCalendarScheduling, CalendarScheduling};
use crate::calendar::{ArchivedCalendarEventNotification, CalendarEventNotification};
use super::{
ArchivedCalendar, ArchivedCalendarEvent, ArchivedCalendarPreferences, ArchivedDefaultAlert,
@@ -22,7 +22,6 @@ impl IndexableObject for Calendar {
IndexValue::Quota {
used: self.dead_properties.size() as u32
+ self.preferences.iter().map(|p| p.size()).sum::<usize>() as u32
+ self.default_alerts.iter().map(|a| a.size()).sum::<usize>() as u32
+ self.name.len() as u32,
},
IndexValue::LogContainer {
@@ -47,7 +46,6 @@ impl IndexableObject for &ArchivedCalendar {
IndexValue::Quota {
used: self.dead_properties.size() as u32
+ self.preferences.iter().map(|p| p.size()).sum::<usize>() as u32
+ self.default_alerts.iter().map(|a| a.size()).sum::<usize>() as u32
+ self.name.len() as u32,
},
IndexValue::LogContainer {
@@ -114,7 +112,7 @@ impl IndexableAndSerializableObject for CalendarEvent {
}
}
impl IndexableObject for CalendarScheduling {
impl IndexableObject for CalendarEventNotification {
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
[
IndexValue::Quota { used: self.size },
@@ -123,7 +121,7 @@ impl IndexableObject for CalendarScheduling {
value: self.created.into(),
},
IndexValue::LogItem {
sync_collection: SyncCollection::CalendarScheduling,
sync_collection: SyncCollection::CalendarEventNotification,
prefix: None,
},
]
@@ -131,7 +129,7 @@ impl IndexableObject for CalendarScheduling {
}
}
impl IndexableObject for &ArchivedCalendarScheduling {
impl IndexableObject for &ArchivedCalendarEventNotification {
fn index_values(&self) -> impl Iterator<Item = IndexValue<'_>> {
[
IndexValue::Quota {
@@ -142,7 +140,7 @@ impl IndexableObject for &ArchivedCalendarScheduling {
value: self.created.to_native().into(),
},
IndexValue::LogItem {
sync_collection: SyncCollection::CalendarScheduling,
sync_collection: SyncCollection::CalendarEventNotification,
prefix: None,
},
]
@@ -150,7 +148,7 @@ impl IndexableObject for &ArchivedCalendarScheduling {
}
}
impl IndexableAndSerializableObject for CalendarScheduling {
impl IndexableAndSerializableObject for CalendarEventNotification {
fn is_versioned() -> bool {
false
}
@@ -159,6 +157,7 @@ impl IndexableAndSerializableObject for CalendarScheduling {
impl CalendarPreferences {
pub fn size(&self) -> usize {
self.name.len()
+ self.default_alerts.iter().map(|a| a.size()).sum::<usize>()
+ self.description.as_ref().map_or(0, |n| n.len())
+ self.color.as_ref().map_or(0, |n| n.len())
+ self.time_zone.size()
@@ -168,6 +167,7 @@ impl CalendarPreferences {
impl ArchivedCalendarPreferences {
pub fn size(&self) -> usize {
self.name.len()
+ self.default_alerts.iter().map(|a| a.size()).sum::<usize>()
+ self.description.as_ref().map_or(0, |n| n.len())
+ self.color.as_ref().map_or(0, |n| n.len())
+ self.time_zone.size()
@@ -196,12 +196,12 @@ impl ArchivedTimezone {
impl DefaultAlert {
pub fn size(&self) -> usize {
self.alert.size() + self.id.len()
std::mem::size_of::<Self>() + self.id.len()
}
}
impl ArchivedDefaultAlert {
pub fn size(&self) -> usize {
self.alert.size() + self.id.len()
std::mem::size_of::<Self>() + self.id.len()
}
}

View File

@@ -7,7 +7,7 @@
use crate::{
RFC_3986,
cache::GroupwareCache,
calendar::{CalendarEvent, CalendarEventData, CalendarScheduling},
calendar::{CalendarEvent, CalendarEventData, CalendarEventNotification},
scheduling::{
ItipError, ItipMessage,
inbound::{
@@ -224,10 +224,10 @@ impl ItipIngest for Server {
// Build event for schedule inbox
let itip_document_id = self
.store()
.assign_document_ids(account_id, Collection::CalendarScheduling, 1)
.assign_document_ids(account_id, Collection::CalendarEventNotification, 1)
.await
.caused_by(trc::location!())?;
let itip_message = CalendarScheduling {
let itip_message = CalendarEventNotification {
itip,
event_id: Some(document_id),
size: itip_message.len() as u32,
@@ -329,10 +329,10 @@ impl ItipIngest for Server {
.caused_by(trc::location!())?;
let itip_document_id = self
.store()
.assign_document_ids(account_id, Collection::CalendarScheduling, 1)
.assign_document_ids(account_id, Collection::CalendarEventNotification, 1)
.await
.caused_by(trc::location!())?;
let itip_message = CalendarScheduling {
let itip_message = CalendarEventNotification {
itip,
event_id: Some(document_id),
size: itip_message.len() as u32,

View File

@@ -11,7 +11,7 @@ pub mod index;
pub mod itip;
pub mod storage;
use calcard::icalendar::ICalendar;
use calcard::icalendar::{ICalendar, ICalendarDuration};
use common::{DavName, auth::AccessToken};
use dav_proto::schema::request::DeadProperty;
use types::acl::AclGrant;
@@ -22,7 +22,6 @@ use types::acl::AclGrant;
pub struct Calendar {
pub name: String,
pub preferences: Vec<CalendarPreferences>,
pub default_alerts: Vec<DefaultAlert>,
pub acls: Vec<AclGrant>,
pub dead_properties: DeadProperty,
pub created: i64,
@@ -46,6 +45,7 @@ pub struct CalendarPreferences {
pub color: Option<String>,
pub flags: u16,
pub time_zone: Timezone,
pub default_alerts: Vec<DefaultAlert>,
}
#[derive(
@@ -54,10 +54,14 @@ pub struct CalendarPreferences {
pub struct DefaultAlert {
pub account_id: u32,
pub id: String,
pub alert: ICalendar,
pub with_time: bool,
pub offset: ICalendarDuration,
pub flags: u16,
}
pub const ALERT_WITH_TIME: u16 = 1;
pub const ALERT_EMAIL: u16 = 1 << 1;
pub const ALERT_RELATIVE_TO_END: u16 = 1 << 2;
pub const SCHEDULE_INBOX_ID: u32 = u32::MAX - 1;
pub const SCHEDULE_OUTBOX_ID: u32 = u32::MAX - 2;
@@ -86,7 +90,7 @@ pub struct CalendarEvent {
#[derive(
rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Debug, Default, Clone, PartialEq, Eq,
)]
pub struct CalendarScheduling {
pub struct CalendarEventNotification {
pub itip: ICalendar,
pub event_id: Option<u32>,
pub flags: u16,

View File

@@ -10,7 +10,7 @@ use super::{
};
use crate::{
DavResourceName, DestroyArchive, RFC_3986,
calendar::{ArchivedCalendarScheduling, CalendarScheduling},
calendar::{ArchivedCalendarEventNotification, CalendarEventNotification},
scheduling::{ItipMessages, event_cancel::itip_cancel},
};
use calcard::common::timezone::Tz;
@@ -47,14 +47,14 @@ impl ItipAutoExpunge for Server {
IterateParams::new(
IndexKey {
account_id,
collection: Collection::CalendarScheduling.into(),
collection: Collection::CalendarEventNotification.into(),
document_id: 0,
field: CalendarField::Created.into(),
key: 0u64.serialize(),
},
IndexKey {
account_id,
collection: Collection::CalendarScheduling.into(),
collection: Collection::CalendarEventNotification.into(),
document_id: u32::MAX,
field: CalendarField::Created.into(),
key: now().saturating_sub(hold_period).serialize(),
@@ -81,7 +81,7 @@ impl ItipAutoExpunge for Server {
trc::event!(
Purge(trc::PurgeEvent::AutoExpunge),
AccountId = account_id,
Collection = Collection::CalendarScheduling.as_str(),
Collection = Collection::CalendarEventNotification.as_str(),
Total = destroy_ids.len(),
);
@@ -95,12 +95,16 @@ impl ItipAutoExpunge for Server {
for document_id in destroy_ids {
// Fetch event
if let Some(event_) = self
.get_archive(account_id, Collection::CalendarScheduling, document_id)
.get_archive(
account_id,
Collection::CalendarEventNotification,
document_id,
)
.await
.caused_by(trc::location!())?
{
let event = event_
.to_unarchived::<CalendarScheduling>()
.to_unarchived::<CalendarEventNotification>()
.caused_by(trc::location!())?;
DestroyArchive(event)
.delete(&access_token, account_id, document_id, &mut batch)
@@ -238,7 +242,7 @@ impl Calendar {
}
}
impl CalendarScheduling {
impl CalendarEventNotification {
pub fn insert<'x>(
self,
access_token: &AccessToken,
@@ -255,7 +259,7 @@ impl CalendarScheduling {
// Prepare write batch
batch
.with_account_id(account_id)
.with_collection(Collection::CalendarScheduling)
.with_collection(Collection::CalendarEventNotification)
.create_document(document_id)
.custom(
ObjectIndexBuilder::<(), _>::new()
@@ -421,7 +425,7 @@ impl DestroyArchive<Archive<&ArchivedCalendarEvent>> {
}
}
impl DestroyArchive<Archive<&ArchivedCalendarScheduling>> {
impl DestroyArchive<Archive<&ArchivedCalendarEventNotification>> {
#[allow(clippy::too_many_arguments)]
pub fn delete(
self,
@@ -433,7 +437,7 @@ impl DestroyArchive<Archive<&ArchivedCalendarScheduling>> {
// Delete event
batch
.with_account_id(account_id)
.with_collection(Collection::CalendarScheduling)
.with_collection(Collection::CalendarEventNotification)
.delete_document(document_id)
.custom(
ObjectIndexBuilder::<_, ()>::new()

View File

@@ -106,7 +106,7 @@ impl From<DavResourceName> for Collection {
DavResourceName::Cal => Collection::Calendar,
DavResourceName::File => Collection::FileNode,
DavResourceName::Principal => Collection::Principal,
DavResourceName::Scheduling => Collection::CalendarScheduling,
DavResourceName::Scheduling => Collection::CalendarEventNotification,
}
}
}
@@ -118,7 +118,7 @@ impl From<Collection> for DavResourceName {
Collection::Calendar => DavResourceName::Cal,
Collection::FileNode => DavResourceName::File,
Collection::Principal => DavResourceName::Principal,
Collection::CalendarScheduling => DavResourceName::Scheduling,
Collection::CalendarEventNotification => DavResourceName::Scheduling,
_ => unreachable!(),
}
}
@@ -130,7 +130,7 @@ impl From<SyncCollection> for DavResourceName {
SyncCollection::AddressBook => DavResourceName::Card,
SyncCollection::Calendar => DavResourceName::Cal,
SyncCollection::FileNode => DavResourceName::File,
SyncCollection::CalendarScheduling => DavResourceName::Scheduling,
SyncCollection::CalendarEventNotification => DavResourceName::Scheduling,
_ => unreachable!(),
}
}