debug support of calendar-data pruning

This commit is contained in:
Quentin 2024-05-27 08:03:21 +02:00
parent 68e08bed4f
commit 418adf92be
Signed by: quentin
GPG key ID: E9602264D639FF68
6 changed files with 160 additions and 37 deletions

View file

@ -4,5 +4,5 @@
/// the goal will be to rewrite it in the end so it better /// the goal will be to rewrite it in the end so it better
/// integrates into Aerogramme /// integrates into Aerogramme
pub mod parser; pub mod parser;
pub mod query;
pub mod prune; pub mod prune;
pub mod query;

View file

@ -1,40 +1,55 @@
use icalendar::parser::{Component, Property};
use aero_dav::caltypes as cal; use aero_dav::caltypes as cal;
use icalendar::parser::{Component, Property};
pub fn component<'a>(src: &'a Component<'a>, prune: &cal::Comp) -> Option<Component<'a>> { pub fn component<'a>(src: &'a Component<'a>, prune: &cal::Comp) -> Option<Component<'a>> {
if src.name.as_str() != prune.name.as_str() { if src.name.as_str() != prune.name.as_str() {
return None return None;
} }
let name = src.name.clone(); let name = src.name.clone();
let properties = match &prune.prop_kind { let properties = match &prune.prop_kind {
None => vec![], Some(cal::PropKind::AllProp) | None => src.properties.clone(),
Some(cal::PropKind::AllProp) => src.properties.clone(), Some(cal::PropKind::Prop(l)) => src
Some(cal::PropKind::Prop(l)) => src.properties.iter().filter_map(|prop| { .properties
let sel_filt = match l.iter().find(|filt| filt.name.0.as_str() == prop.name.as_str()) { .iter()
Some(v) => v, .filter_map(|prop| {
None => return None let sel_filt = match l
}; .iter()
.find(|filt| filt.name.0.as_str() == prop.name.as_str())
{
Some(v) => v,
None => return None,
};
match sel_filt.novalue { match sel_filt.novalue {
None | Some(false) => Some(prop.clone()), None | Some(false) => Some(prop.clone()),
Some(true) => Some(Property { Some(true) => Some(Property {
name: prop.name.clone(), name: prop.name.clone(),
params: prop.params.clone(), params: prop.params.clone(),
val: "".into() val: "".into(),
}), }),
} }
}).collect::<Vec<_>>(), })
.collect::<Vec<_>>(),
}; };
let components = match &prune.comp_kind { let components = match &prune.comp_kind {
None => vec![], Some(cal::CompKind::AllComp) | None => src.components.clone(),
Some(cal::CompKind::AllComp) => src.components.clone(), Some(cal::CompKind::Comp(many_inner_prune)) => src
Some(cal::CompKind::Comp(many_inner_prune)) => src.components.iter().filter_map(|src_component| { .components
many_inner_prune.iter().find_map(|inner_prune| component(src_component, inner_prune)) .iter()
}).collect::<Vec<_>>(), .filter_map(|src_component| {
many_inner_prune
.iter()
.find_map(|inner_prune| component(src_component, inner_prune))
})
.collect::<Vec<_>>(),
}; };
Some(Component { name, properties, components }) Some(Component {
name,
properties,
components,
})
} }

View file

@ -333,7 +333,6 @@ impl<'a> Path<'a> {
} }
} }
//@FIXME move somewhere else
//@FIXME naive implementation, must be refactored later //@FIXME naive implementation, must be refactored later
use futures::stream::Stream; use futures::stream::Stream;
fn apply_filter<'a>( fn apply_filter<'a>(

View file

@ -565,17 +565,49 @@ impl DavNode for EventNode {
dav::Property::GetEtag(etag) dav::Property::GetEtag(etag)
} }
dav::PropertyRequest::Extension(all::PropertyRequest::Cal( dav::PropertyRequest::Extension(all::PropertyRequest::Cal(
cal::PropertyRequest::CalendarData(_req), cal::PropertyRequest::CalendarData(req),
)) => { )) => {
let ics = String::from_utf8( let ics = String::from_utf8(
this.col.get(this.blob_id).await.or(Err(n.clone()))?, this.col.get(this.blob_id).await.or(Err(n.clone()))?,
) )
.or(Err(n.clone()))?; .or(Err(n.clone()))?;
let new_ics = match &req.comp {
None => ics,
Some(prune_comp) => {
// parse content
let ics = match icalendar::parser::read_calendar(&ics) {
Ok(v) => v,
Err(e) => {
tracing::warn!(err=?e, "Unable to parse ICS in calendar-query");
return Err(n.clone())
}
};
// build a fake vcal component for caldav compat
let fake_vcal_component = icalendar::parser::Component {
name: cal::Component::VCalendar.as_str().into(),
properties: ics.properties,
components: ics.components,
};
// rebuild component
let new_comp = match aero_ical::prune::component(&fake_vcal_component, prune_comp) {
Some(v) => v,
None => return Err(n.clone()),
};
// reserialize
format!("{}", icalendar::parser::Calendar { properties: new_comp.properties, components: new_comp.components })
},
};
dav::Property::Extension(all::Property::Cal( dav::Property::Extension(all::Property::Cal(
cal::Property::CalendarData(cal::CalendarDataPayload { cal::Property::CalendarData(cal::CalendarDataPayload {
mime: None, mime: None,
payload: ics, payload: new_ics,
}), }),
)) ))
} }
@ -634,14 +666,15 @@ impl DavNode for EventNode {
// so we load everything in memory // so we load everything in memory
let calendar = self.col.clone(); let calendar = self.col.clone();
let blob_id = self.blob_id.clone(); let blob_id = self.blob_id.clone();
let r = async move { let calblob = async move {
let content = calendar let raw_ics = calendar
.get(blob_id) .get(blob_id)
.await .await
.or(Err(std::io::Error::from(std::io::ErrorKind::Interrupted))); .or(Err(std::io::Error::from(std::io::ErrorKind::Interrupted)))?;
Ok(hyper::body::Bytes::from(content?))
Ok(hyper::body::Bytes::from(raw_ics))
}; };
futures::stream::once(Box::pin(r)).boxed() futures::stream::once(Box::pin(calblob)).boxed()
} }
fn content_type(&self) -> &str { fn content_type(&self) -> &str {

View file

@ -956,7 +956,55 @@ fn rfc4791_webdav_caldav() {
); );
// --- REPORT calendar-query, with calendar-data tx --- // --- REPORT calendar-query, with calendar-data tx ---
//@FIXME add support for calendar-data... let cal_query = r#"<?xml version="1.0" encoding="utf-8" ?>
<C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
<D:prop>
<D:getetag/>
<C:calendar-data>
<C:comp name="VCALENDAR">
<C:prop name="VERSION"/>
<C:comp name="VEVENT">
<C:prop name="UID"/>
<C:prop name="DTSTART"/>
<C:prop name="DTEND"/>
<C:prop name="DURATION"/>
<C:prop name="RRULE"/>
<C:prop name="RDATE"/>
<C:prop name="EXRULE"/>
<C:prop name="EXDATE"/>
<C:prop name="RECURRENCE-ID"/>
</C:comp>
<C:comp name="VTIMEZONE"/>
</C:comp>
</C:calendar-data>
</D:prop>
<C:filter>
<C:comp-filter name="VCALENDAR">
<C:comp-filter name="VEVENT">
<C:time-range start="20060104T000000Z" end="20060105T000000Z"/>
</C:comp-filter>
</C:comp-filter>
</C:filter>
</C:calendar-query>"#;
let resp = http
.request(
reqwest::Method::from_bytes(b"REPORT")?,
"http://localhost:8087/alice/calendar/Personal/",
)
.body(cal_query)
.send()?;
assert_eq!(resp.status(), 207);
let multistatus = dav_deserialize::<dav::Multistatus<All>>(&resp.text()?);
assert_eq!(multistatus.responses.len(), 1);
check_cal(
&multistatus,
(
"/alice/calendar/Personal/rfc3.ics",
Some(obj3_etag.to_str().expect("etag header convertible to str")),
Some(ICAL_RFC3_STRIPPED),
),
);
Ok(()) Ok(())
}) })

View file

@ -125,6 +125,34 @@ END:VEVENT
END:VCALENDAR END:VCALENDAR
"; ";
pub static ICAL_RFC3_STRIPPED: &[u8] = b"BEGIN:VCALENDAR\r
VERSION:2.0\r
BEGIN:VTIMEZONE\r
LAST-MODIFIED:20040110T032845Z\r
TZID:US/Eastern\r
BEGIN:DAYLIGHT\r
DTSTART:20000404T020000\r
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4\r
TZNAME:EDT\r
TZOFFSETFROM:-0500\r
TZOFFSETTO:-0400\r
END:DAYLIGHT\r
BEGIN:STANDARD\r
DTSTART:20001026T020000\r
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10\r
TZNAME:EST\r
TZOFFSETFROM:-0400\r
TZOFFSETTO:-0500\r
END:STANDARD\r
END:VTIMEZONE\r
BEGIN:VEVENT\r
DTSTART;TZID=US/Eastern:20060104T100000\r
DURATION:PT1H\r
UID:DC6C50A017428C5216A2F1CD@example.com\r
END:VEVENT\r
END:VCALENDAR\r
";
pub static ICAL_RFC4: &[u8] = br#"BEGIN:VCALENDAR pub static ICAL_RFC4: &[u8] = br#"BEGIN:VCALENDAR
VERSION:2.0 VERSION:2.0
PRODID:-//Example Corp.//CalDAV Client//EN PRODID:-//Example Corp.//CalDAV Client//EN