From b6c656de8f8e8caf75dfe3bea9096576f3263cf4 Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Sat, 20 Apr 2024 20:01:07 +0200 Subject: [PATCH] Add a virtual node CreateEventNode --- aero-proto/src/dav/controller.rs | 36 ++++++++++--- aero-proto/src/dav/node.rs | 11 +++- aero-proto/src/dav/resource.rs | 88 ++++++++++++++++++++++++-------- 3 files changed, 105 insertions(+), 30 deletions(-) diff --git a/aero-proto/src/dav/controller.rs b/aero-proto/src/dav/controller.rs index 79ead0a..243a455 100644 --- a/aero-proto/src/dav/controller.rs +++ b/aero-proto/src/dav/controller.rs @@ -2,6 +2,7 @@ use anyhow::Result; use http_body_util::combinators::BoxBody; use hyper::body::Incoming; use hyper::{Request, Response, body::Bytes}; +use http_body_util::StreamBody; use aero_collections::user::User; use aero_dav::types as dav; @@ -39,7 +40,8 @@ impl Controller { let path_segments: Vec<_> = path.split("/").filter(|s| *s != "").collect(); let method = req.method().as_str().to_uppercase(); - let node = match (RootNode {}).fetch(&user, &path_segments).await { + let can_create = matches!(method.as_str(), "PUT" | "MKCOL" | "MKCALENDAR"); + let node = match (RootNode {}).fetch(&user, &path_segments, can_create).await{ Ok(v) => v, Err(e) => { tracing::warn!(err=?e, "dav node fetch failed"); @@ -48,6 +50,7 @@ impl Controller { .body(codec::text_body("Resource not found"))?) } }; + let ctrl = Self { node, user, req }; match method.as_str() { @@ -63,7 +66,8 @@ impl Controller { .body(codec::text_body(""))?) }, "PUT" => { - todo!(); + let to_create = path_segments.last().expect("Bound checked earlier in this fx"); + ctrl.put(to_create).await }, "DELETE" => { todo!(); @@ -77,7 +81,7 @@ impl Controller { } - // --- Public API --- + // --- Per-method functions --- /// REPORT has been first described in the "Versioning Extension" of WebDAV /// It allows more complex queries compared to PROPFIND @@ -111,8 +115,8 @@ impl Controller { let (mut ok_node, mut not_found) = (Vec::new(), Vec::new()); for h in multiget.href.into_iter() { let maybe_collected_node = match Path::new(h.0.as_str()) { - Ok(Path::Abs(p)) => RootNode{}.fetch(&self.user, p.as_slice()).await.or(Err(h)), - Ok(Path::Rel(p)) => self.node.fetch(&self.user, p.as_slice()).await.or(Err(h)), + Ok(Path::Abs(p)) => RootNode{}.fetch(&self.user, p.as_slice(), false).await.or(Err(h)), + Ok(Path::Rel(p)) => self.node.fetch(&self.user, p.as_slice(), false).await.or(Err(h)), Err(_) => Err(h), }; @@ -173,9 +177,25 @@ impl Controller { serialize(status, Self::multistatus(&self.user, nodes, not_found, propname)) } - // --- Internal functions --- - /// Utility function to build a multistatus response from - /// a list of DavNodes + async fn put(self, child: &str) -> Result>> { + todo!() + } + + async fn get(self) -> Result>> { + todo!() + /*let stream = StreamBody::new(self.node.get().map(|v| Ok(Frame::data(v)))); + let boxed_body = BoxBody::new(stream); + + let response = Response::builder() + .status(200) + //.header("content-type", "application/xml; charset=\"utf-8\"") + .body(boxed_body)?; + + Ok(response)*/ + } + + // --- Common utulity functions --- + /// Build a multistatus response from a list of DavNodes fn multistatus(user: &ArcUser, nodes: Vec>, not_found: Vec, props: Option>) -> dav::Multistatus { // Collect properties on existing objects let mut responses: Vec> = match props { diff --git a/aero-proto/src/dav/node.rs b/aero-proto/src/dav/node.rs index afeeeff..b0f97a5 100644 --- a/aero-proto/src/dav/node.rs +++ b/aero-proto/src/dav/node.rs @@ -7,6 +7,11 @@ use aero_collections::user::User; type ArcUser = std::sync::Arc; +pub(crate) enum PutPolicy { + CreateOnly, + ReplaceEtags(String), +} + /// A DAV node should implement the following methods /// @FIXME not satisfied by BoxFutures but I have no better idea currently pub(crate) trait DavNode: Send { @@ -14,7 +19,7 @@ pub(crate) trait DavNode: Send { /// This node direct children fn children<'a>(&self, user: &'a ArcUser) -> BoxFuture<'a, Vec>>; /// Recursively fetch a child (progress inside the filesystem hierarchy) - fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str]) -> BoxFuture<'a, Result>>; + fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str], create: bool) -> BoxFuture<'a, Result>>; // node properties /// Get the path @@ -23,6 +28,10 @@ pub(crate) trait DavNode: Send { fn supported_properties(&self, user: &ArcUser) -> dav::PropName; /// Get the values for the given properties fn properties(&self, user: &ArcUser, prop: dav::PropName) -> Vec>; + /// Put a child + //fn put(&self, policy: PutPolicy, stream: TryStream) -> BoxFuture>; + /// Get content + //fn content(&self) -> TryStream; //@FIXME maybe add etag, maybe add a way to set content diff --git a/aero-proto/src/dav/resource.rs b/aero-proto/src/dav/resource.rs index 9ad662a..fec8bcb 100644 --- a/aero-proto/src/dav/resource.rs +++ b/aero-proto/src/dav/resource.rs @@ -17,7 +17,7 @@ use crate::dav::node::DavNode; #[derive(Clone)] pub(crate) struct RootNode {} impl DavNode for RootNode { - fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str]) -> BoxFuture<'a, Result>> { + fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str], create: bool) -> BoxFuture<'a, Result>> { if path.len() == 0 { let this = self.clone(); return async { Ok(Box::new(this) as Box) }.boxed(); @@ -25,9 +25,10 @@ impl DavNode for RootNode { if path[0] == user.username { let child = Box::new(HomeNode {}); - return child.fetch(user, &path[1..]); + return child.fetch(user, &path[1..], create); } + //@NOTE: We can't create a node at this level async { Err(anyhow!("Not found")) }.boxed() } @@ -64,19 +65,20 @@ impl DavNode for RootNode { #[derive(Clone)] pub(crate) struct HomeNode {} impl DavNode for HomeNode { - fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str]) -> BoxFuture<'a, Result>> { + fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str], create: bool) -> BoxFuture<'a, Result>> { if path.len() == 0 { let node = Box::new(self.clone()) as Box; return async { Ok(node) }.boxed() } if path[0] == "calendar" { - return async { + return async move { let child = Box::new(CalendarListNode::new(user).await?); - child.fetch(user, &path[1..]).await + child.fetch(user, &path[1..], create).await }.boxed(); } - + + //@NOTE: we can't create a node at this level async { Err(anyhow!("Not found")) }.boxed() } @@ -126,19 +128,20 @@ impl CalendarListNode { } } impl DavNode for CalendarListNode { - fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str]) -> BoxFuture<'a, Result>> { + fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str], create: bool) -> BoxFuture<'a, Result>> { if path.len() == 0 { let node = Box::new(self.clone()) as Box; return async { Ok(node) }.boxed(); } - async { + async move { + //@FIXME: we should create a node if the open returns a "not found". let cal = user.calendars.open(user, path[0]).await?.ok_or(anyhow!("Not found"))?; let child = Box::new(CalendarNode { col: cal, calname: path[0].to_string() }); - child.fetch(user, &path[1..]).await + child.fetch(user, &path[1..], create).await }.boxed() } @@ -189,7 +192,7 @@ pub(crate) struct CalendarNode { calname: String, } impl DavNode for CalendarNode { - fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str]) -> BoxFuture<'a, Result>> { + fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str], create: bool) -> BoxFuture<'a, Result>> { if path.len() == 0 { let node = Box::new(self.clone()) as Box; return async { Ok(node) }.boxed() @@ -198,17 +201,27 @@ impl DavNode for CalendarNode { let col = self.col.clone(); let calname = self.calname.clone(); async move { - if let Some(blob_id) = col.dag().await.idx_by_filename.get(path[0]) { - let child = Box::new(EventNode { - col: col.clone(), - calname, - filename: path[0].to_string(), - blob_id: *blob_id, - }); - return child.fetch(user, &path[1..]).await + match (col.dag().await.idx_by_filename.get(path[0]), create) { + (Some(blob_id), _) => { + let child = Box::new(EventNode { + col: col.clone(), + calname, + filename: path[0].to_string(), + blob_id: *blob_id, + }); + child.fetch(user, &path[1..], create).await + }, + (None, true) => { + let child = Box::new(CreateEventNode { + col: col.clone(), + calname, + filename: path[0].to_string(), + }); + child.fetch(user, &path[1..], create).await + }, + _ => Err(anyhow!("Not found")), } - Err(anyhow!("Not found")) }.boxed() } @@ -298,13 +311,13 @@ pub(crate) struct EventNode { blob_id: BlobId, } impl DavNode for EventNode { - fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str]) -> BoxFuture<'a, Result>> { + fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str], create: bool) -> BoxFuture<'a, Result>> { if path.len() == 0 { let node = Box::new(self.clone()) as Box; return async { Ok(node) }.boxed() } - async { Err(anyhow!("Not found")) }.boxed() + async { Err(anyhow!("Not supported: can't create a child on an event node")) }.boxed() } fn children<'a>(&self, user: &'a ArcUser) -> BoxFuture<'a, Vec>> { @@ -338,3 +351,36 @@ impl DavNode for EventNode { }).collect() } } + +#[derive(Clone)] +pub(crate) struct CreateEventNode { + col: Arc, + calname: String, + filename: String, +} +impl DavNode for CreateEventNode { + fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str], create: bool) -> BoxFuture<'a, Result>> { + if path.len() == 0 { + let node = Box::new(self.clone()) as Box; + return async { Ok(node) }.boxed() + } + + async { Err(anyhow!("Not supported: can't create a child on an event node")) }.boxed() + } + + fn children<'a>(&self, user: &'a ArcUser) -> BoxFuture<'a, Vec>> { + async { vec![] }.boxed() + } + + fn path(&self, user: &ArcUser) -> String { + format!("/{}/calendar/{}/{}", user.username, self.calname, self.filename) + } + + fn supported_properties(&self, user: &ArcUser) -> dav::PropName { + dav::PropName(vec![]) + } + + fn properties(&self, _user: &ArcUser, prop: dav::PropName) -> Vec> { + vec![] + } +}