Add a virtual node CreateEventNode
This commit is contained in:
parent
e2bf412337
commit
b6c656de8f
3 changed files with 105 additions and 30 deletions
|
@ -2,6 +2,7 @@ use anyhow::Result;
|
||||||
use http_body_util::combinators::BoxBody;
|
use http_body_util::combinators::BoxBody;
|
||||||
use hyper::body::Incoming;
|
use hyper::body::Incoming;
|
||||||
use hyper::{Request, Response, body::Bytes};
|
use hyper::{Request, Response, body::Bytes};
|
||||||
|
use http_body_util::StreamBody;
|
||||||
|
|
||||||
use aero_collections::user::User;
|
use aero_collections::user::User;
|
||||||
use aero_dav::types as dav;
|
use aero_dav::types as dav;
|
||||||
|
@ -39,7 +40,8 @@ impl Controller {
|
||||||
let path_segments: Vec<_> = path.split("/").filter(|s| *s != "").collect();
|
let path_segments: Vec<_> = path.split("/").filter(|s| *s != "").collect();
|
||||||
let method = req.method().as_str().to_uppercase();
|
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,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::warn!(err=?e, "dav node fetch failed");
|
tracing::warn!(err=?e, "dav node fetch failed");
|
||||||
|
@ -48,6 +50,7 @@ impl Controller {
|
||||||
.body(codec::text_body("Resource not found"))?)
|
.body(codec::text_body("Resource not found"))?)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let ctrl = Self { node, user, req };
|
let ctrl = Self { node, user, req };
|
||||||
|
|
||||||
match method.as_str() {
|
match method.as_str() {
|
||||||
|
@ -63,7 +66,8 @@ impl Controller {
|
||||||
.body(codec::text_body(""))?)
|
.body(codec::text_body(""))?)
|
||||||
},
|
},
|
||||||
"PUT" => {
|
"PUT" => {
|
||||||
todo!();
|
let to_create = path_segments.last().expect("Bound checked earlier in this fx");
|
||||||
|
ctrl.put(to_create).await
|
||||||
},
|
},
|
||||||
"DELETE" => {
|
"DELETE" => {
|
||||||
todo!();
|
todo!();
|
||||||
|
@ -77,7 +81,7 @@ impl Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// --- Public API ---
|
// --- Per-method functions ---
|
||||||
|
|
||||||
/// REPORT has been first described in the "Versioning Extension" of WebDAV
|
/// REPORT has been first described in the "Versioning Extension" of WebDAV
|
||||||
/// It allows more complex queries compared to PROPFIND
|
/// 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());
|
let (mut ok_node, mut not_found) = (Vec::new(), Vec::new());
|
||||||
for h in multiget.href.into_iter() {
|
for h in multiget.href.into_iter() {
|
||||||
let maybe_collected_node = match Path::new(h.0.as_str()) {
|
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::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()).await.or(Err(h)),
|
Ok(Path::Rel(p)) => self.node.fetch(&self.user, p.as_slice(), false).await.or(Err(h)),
|
||||||
Err(_) => Err(h),
|
Err(_) => Err(h),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -173,9 +177,25 @@ impl Controller {
|
||||||
serialize(status, Self::multistatus(&self.user, nodes, not_found, propname))
|
serialize(status, Self::multistatus(&self.user, nodes, not_found, propname))
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Internal functions ---
|
async fn put(self, child: &str) -> Result<Response<BoxBody<Bytes, std::io::Error>>> {
|
||||||
/// Utility function to build a multistatus response from
|
todo!()
|
||||||
/// a list of DavNodes
|
}
|
||||||
|
|
||||||
|
async fn get(self) -> Result<Response<BoxBody<Bytes, std::io::Error>>> {
|
||||||
|
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<Box<dyn DavNode>>, not_found: Vec<dav::Href>, props: Option<dav::PropName<All>>) -> dav::Multistatus<All> {
|
fn multistatus(user: &ArcUser, nodes: Vec<Box<dyn DavNode>>, not_found: Vec<dav::Href>, props: Option<dav::PropName<All>>) -> dav::Multistatus<All> {
|
||||||
// Collect properties on existing objects
|
// Collect properties on existing objects
|
||||||
let mut responses: Vec<dav::Response<All>> = match props {
|
let mut responses: Vec<dav::Response<All>> = match props {
|
||||||
|
|
|
@ -7,6 +7,11 @@ use aero_collections::user::User;
|
||||||
|
|
||||||
type ArcUser = std::sync::Arc<User>;
|
type ArcUser = std::sync::Arc<User>;
|
||||||
|
|
||||||
|
pub(crate) enum PutPolicy {
|
||||||
|
CreateOnly,
|
||||||
|
ReplaceEtags(String),
|
||||||
|
}
|
||||||
|
|
||||||
/// A DAV node should implement the following methods
|
/// A DAV node should implement the following methods
|
||||||
/// @FIXME not satisfied by BoxFutures but I have no better idea currently
|
/// @FIXME not satisfied by BoxFutures but I have no better idea currently
|
||||||
pub(crate) trait DavNode: Send {
|
pub(crate) trait DavNode: Send {
|
||||||
|
@ -14,7 +19,7 @@ pub(crate) trait DavNode: Send {
|
||||||
/// This node direct children
|
/// This node direct children
|
||||||
fn children<'a>(&self, user: &'a ArcUser) -> BoxFuture<'a, Vec<Box<dyn DavNode>>>;
|
fn children<'a>(&self, user: &'a ArcUser) -> BoxFuture<'a, Vec<Box<dyn DavNode>>>;
|
||||||
/// Recursively fetch a child (progress inside the filesystem hierarchy)
|
/// Recursively fetch a child (progress inside the filesystem hierarchy)
|
||||||
fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str]) -> BoxFuture<'a, Result<Box<dyn DavNode>>>;
|
fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str], create: bool) -> BoxFuture<'a, Result<Box<dyn DavNode>>>;
|
||||||
|
|
||||||
// node properties
|
// node properties
|
||||||
/// Get the path
|
/// Get the path
|
||||||
|
@ -23,6 +28,10 @@ pub(crate) trait DavNode: Send {
|
||||||
fn supported_properties(&self, user: &ArcUser) -> dav::PropName<All>;
|
fn supported_properties(&self, user: &ArcUser) -> dav::PropName<All>;
|
||||||
/// Get the values for the given properties
|
/// Get the values for the given properties
|
||||||
fn properties(&self, user: &ArcUser, prop: dav::PropName<All>) -> Vec<dav::AnyProperty<All>>;
|
fn properties(&self, user: &ArcUser, prop: dav::PropName<All>) -> Vec<dav::AnyProperty<All>>;
|
||||||
|
/// Put a child
|
||||||
|
//fn put(&self, policy: PutPolicy, stream: TryStream) -> BoxFuture<Result<dyn DavNode>>;
|
||||||
|
/// Get content
|
||||||
|
//fn content(&self) -> TryStream;
|
||||||
|
|
||||||
//@FIXME maybe add etag, maybe add a way to set content
|
//@FIXME maybe add etag, maybe add a way to set content
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ use crate::dav::node::DavNode;
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct RootNode {}
|
pub(crate) struct RootNode {}
|
||||||
impl DavNode for RootNode {
|
impl DavNode for RootNode {
|
||||||
fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str]) -> BoxFuture<'a, Result<Box<dyn DavNode>>> {
|
fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str], create: bool) -> BoxFuture<'a, Result<Box<dyn DavNode>>> {
|
||||||
if path.len() == 0 {
|
if path.len() == 0 {
|
||||||
let this = self.clone();
|
let this = self.clone();
|
||||||
return async { Ok(Box::new(this) as Box<dyn DavNode>) }.boxed();
|
return async { Ok(Box::new(this) as Box<dyn DavNode>) }.boxed();
|
||||||
|
@ -25,9 +25,10 @@ impl DavNode for RootNode {
|
||||||
|
|
||||||
if path[0] == user.username {
|
if path[0] == user.username {
|
||||||
let child = Box::new(HomeNode {});
|
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()
|
async { Err(anyhow!("Not found")) }.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,19 +65,20 @@ impl DavNode for RootNode {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct HomeNode {}
|
pub(crate) struct HomeNode {}
|
||||||
impl DavNode for HomeNode {
|
impl DavNode for HomeNode {
|
||||||
fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str]) -> BoxFuture<'a, Result<Box<dyn DavNode>>> {
|
fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str], create: bool) -> BoxFuture<'a, Result<Box<dyn DavNode>>> {
|
||||||
if path.len() == 0 {
|
if path.len() == 0 {
|
||||||
let node = Box::new(self.clone()) as Box<dyn DavNode>;
|
let node = Box::new(self.clone()) as Box<dyn DavNode>;
|
||||||
return async { Ok(node) }.boxed()
|
return async { Ok(node) }.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
if path[0] == "calendar" {
|
if path[0] == "calendar" {
|
||||||
return async {
|
return async move {
|
||||||
let child = Box::new(CalendarListNode::new(user).await?);
|
let child = Box::new(CalendarListNode::new(user).await?);
|
||||||
child.fetch(user, &path[1..]).await
|
child.fetch(user, &path[1..], create).await
|
||||||
}.boxed();
|
}.boxed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//@NOTE: we can't create a node at this level
|
||||||
async { Err(anyhow!("Not found")) }.boxed()
|
async { Err(anyhow!("Not found")) }.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,19 +128,20 @@ impl CalendarListNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl DavNode for CalendarListNode {
|
impl DavNode for CalendarListNode {
|
||||||
fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str]) -> BoxFuture<'a, Result<Box<dyn DavNode>>> {
|
fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str], create: bool) -> BoxFuture<'a, Result<Box<dyn DavNode>>> {
|
||||||
if path.len() == 0 {
|
if path.len() == 0 {
|
||||||
let node = Box::new(self.clone()) as Box<dyn DavNode>;
|
let node = Box::new(self.clone()) as Box<dyn DavNode>;
|
||||||
return async { Ok(node) }.boxed();
|
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 cal = user.calendars.open(user, path[0]).await?.ok_or(anyhow!("Not found"))?;
|
||||||
let child = Box::new(CalendarNode {
|
let child = Box::new(CalendarNode {
|
||||||
col: cal,
|
col: cal,
|
||||||
calname: path[0].to_string()
|
calname: path[0].to_string()
|
||||||
});
|
});
|
||||||
child.fetch(user, &path[1..]).await
|
child.fetch(user, &path[1..], create).await
|
||||||
}.boxed()
|
}.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,7 +192,7 @@ pub(crate) struct CalendarNode {
|
||||||
calname: String,
|
calname: String,
|
||||||
}
|
}
|
||||||
impl DavNode for CalendarNode {
|
impl DavNode for CalendarNode {
|
||||||
fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str]) -> BoxFuture<'a, Result<Box<dyn DavNode>>> {
|
fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str], create: bool) -> BoxFuture<'a, Result<Box<dyn DavNode>>> {
|
||||||
if path.len() == 0 {
|
if path.len() == 0 {
|
||||||
let node = Box::new(self.clone()) as Box<dyn DavNode>;
|
let node = Box::new(self.clone()) as Box<dyn DavNode>;
|
||||||
return async { Ok(node) }.boxed()
|
return async { Ok(node) }.boxed()
|
||||||
|
@ -198,17 +201,27 @@ impl DavNode for CalendarNode {
|
||||||
let col = self.col.clone();
|
let col = self.col.clone();
|
||||||
let calname = self.calname.clone();
|
let calname = self.calname.clone();
|
||||||
async move {
|
async move {
|
||||||
if let Some(blob_id) = col.dag().await.idx_by_filename.get(path[0]) {
|
match (col.dag().await.idx_by_filename.get(path[0]), create) {
|
||||||
|
(Some(blob_id), _) => {
|
||||||
let child = Box::new(EventNode {
|
let child = Box::new(EventNode {
|
||||||
col: col.clone(),
|
col: col.clone(),
|
||||||
calname,
|
calname,
|
||||||
filename: path[0].to_string(),
|
filename: path[0].to_string(),
|
||||||
blob_id: *blob_id,
|
blob_id: *blob_id,
|
||||||
});
|
});
|
||||||
return child.fetch(user, &path[1..]).await
|
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()
|
}.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,13 +311,13 @@ pub(crate) struct EventNode {
|
||||||
blob_id: BlobId,
|
blob_id: BlobId,
|
||||||
}
|
}
|
||||||
impl DavNode for EventNode {
|
impl DavNode for EventNode {
|
||||||
fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str]) -> BoxFuture<'a, Result<Box<dyn DavNode>>> {
|
fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str], create: bool) -> BoxFuture<'a, Result<Box<dyn DavNode>>> {
|
||||||
if path.len() == 0 {
|
if path.len() == 0 {
|
||||||
let node = Box::new(self.clone()) as Box<dyn DavNode>;
|
let node = Box::new(self.clone()) as Box<dyn DavNode>;
|
||||||
return async { Ok(node) }.boxed()
|
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<Box<dyn DavNode>>> {
|
fn children<'a>(&self, user: &'a ArcUser) -> BoxFuture<'a, Vec<Box<dyn DavNode>>> {
|
||||||
|
@ -338,3 +351,36 @@ impl DavNode for EventNode {
|
||||||
}).collect()
|
}).collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub(crate) struct CreateEventNode {
|
||||||
|
col: Arc<Calendar>,
|
||||||
|
calname: String,
|
||||||
|
filename: String,
|
||||||
|
}
|
||||||
|
impl DavNode for CreateEventNode {
|
||||||
|
fn fetch<'a>(&self, user: &'a ArcUser, path: &'a [&str], create: bool) -> BoxFuture<'a, Result<Box<dyn DavNode>>> {
|
||||||
|
if path.len() == 0 {
|
||||||
|
let node = Box::new(self.clone()) as Box<dyn DavNode>;
|
||||||
|
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<Box<dyn DavNode>>> {
|
||||||
|
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<All> {
|
||||||
|
dav::PropName(vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn properties(&self, _user: &ArcUser, prop: dav::PropName<All>) -> Vec<dav::AnyProperty<All>> {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue