Improve routing

This commit is contained in:
Lennart
2025-06-09 16:30:14 +02:00
parent 764d049d3c
commit 8f29a468db
9 changed files with 99 additions and 71 deletions

View File

@@ -1,9 +1,10 @@
use super::prop::{SupportedCalendarComponentSet, SupportedCalendarData, SupportedReportSet}; use super::prop::{SupportedCalendarComponentSet, SupportedCalendarData, SupportedReportSet};
use crate::calendar::methods::mkcalendar::route_mkcalendar; use crate::calendar::methods::mkcalendar::route_mkcalendar;
use crate::calendar::methods::report::route_report_calendar; use crate::calendar::methods::report::route_report_calendar;
use crate::calendar_object::resource::CalendarObjectResource; use crate::calendar_object::resource::{CalendarObjectResource, CalendarObjectResourceService};
use crate::{CalDavPrincipalUri, Error}; use crate::{CalDavPrincipalUri, Error};
use async_trait::async_trait; use async_trait::async_trait;
use axum::Router;
use axum::extract::Request; use axum::extract::Request;
use axum::handler::Handler; use axum::handler::Handler;
use axum::response::Response; use axum::response::Response;
@@ -398,6 +399,15 @@ impl<C: CalendarStore, S: SubscriptionStore> ResourceService for CalendarResourc
.await?; .await?;
Ok(()) Ok(())
} }
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> axum::Router<State> {
Router::new()
.nest(
"/{object_id}",
CalendarObjectResourceService::new(self.cal_store.clone()).axum_router(),
)
.route_service("/", self.axum_service())
}
} }
impl<C: CalendarStore, S: SubscriptionStore> AxumMethods for CalendarResourceService<C, S> { impl<C: CalendarStore, S: SubscriptionStore> AxumMethods for CalendarResourceService<C, S> {

View File

@@ -1,6 +1,7 @@
use crate::calendar::resource::CalendarResource; use crate::calendar::resource::{CalendarResource, CalendarResourceService};
use crate::{CalDavPrincipalUri, Error}; use crate::{CalDavPrincipalUri, Error};
use async_trait::async_trait; use async_trait::async_trait;
use axum::Router;
use rustical_dav::extensions::{CommonPropertiesExtension, CommonPropertiesProp}; use rustical_dav::extensions::{CommonPropertiesExtension, CommonPropertiesProp};
use rustical_dav::privileges::UserPrivilegeSet; use rustical_dav::privileges::UserPrivilegeSet;
use rustical_dav::resource::{AxumMethods, PrincipalUri, Resource, ResourceService}; use rustical_dav::resource::{AxumMethods, PrincipalUri, Resource, ResourceService};
@@ -125,5 +126,15 @@ impl<C: CalendarStore, S: SubscriptionStore> ResourceService for CalendarSetReso
}) })
.collect()) .collect())
} }
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> axum::Router<State> {
Router::new()
.nest(
"/{calendar_id}",
CalendarResourceService::new(self.cal_store.clone(), self.sub_store.clone())
.axum_router(),
)
.route_service("/", self.axum_service())
}
} }
impl<C: CalendarStore, S: SubscriptionStore> AxumMethods for CalendarSetResourceService<C, S> {} impl<C: CalendarStore, S: SubscriptionStore> AxumMethods for CalendarSetResourceService<C, S> {}

View File

@@ -17,10 +17,6 @@ pub mod principal;
pub use error::Error; pub use error::Error;
use crate::calendar::resource::CalendarResourceService;
use crate::calendar_object::resource::CalendarObjectResourceService;
use crate::calendar_set::CalendarSetResourceService;
#[derive(Debug, Clone, Constructor)] #[derive(Debug, Clone, Constructor)]
pub struct CalDavPrincipalUri(&'static str); pub struct CalDavPrincipalUri(&'static str);
@@ -50,44 +46,8 @@ pub fn caldav_router<
cal_store: store.clone(), cal_store: store.clone(),
}; };
Router::new()
.route_service(
"/",
RootResourceService::<_, User, CalDavPrincipalUri>::new(principal_service.clone()) RootResourceService::<_, User, CalDavPrincipalUri>::new(principal_service.clone())
.axum_service(), .axum_router()
)
.route_service("/principal/{principal}", principal_service.axum_service())
.route_service(
"/principal/{principal}/calendar",
CalendarSetResourceService::new("calendar", store.clone(), subscription_store.clone())
.axum_service(),
)
.route_service(
"/principal/{principal}/calendar/{calendar_id}",
CalendarResourceService::new(store.clone(), subscription_store.clone()).axum_service(),
)
.route_service(
"/principal/{principal}/calendar/{calendar_id}/{object_id}",
CalendarObjectResourceService::new(store.clone()).axum_service(),
)
.route_service(
"/principal/{principal}/birthdays",
CalendarSetResourceService::new(
"birthdays",
birthday_store.clone(),
subscription_store.clone(),
)
.axum_service(),
)
.route_service(
"/principal/{principal}/birthdays/{calendar_id}",
CalendarResourceService::new(birthday_store.clone(), subscription_store.clone())
.axum_service(),
)
.route_service(
"/principal/{principal}/birthdays/{calendar_id}/{object_id}",
CalendarObjectResourceService::new(birthday_store.clone()).axum_service(),
)
.layer(AuthenticationLayer::new(auth_provider)) .layer(AuthenticationLayer::new(auth_provider))
.layer(Extension(CalDavPrincipalUri(prefix))) .layer(Extension(CalDavPrincipalUri(prefix)))
} }

View File

@@ -1,6 +1,7 @@
use crate::calendar_set::CalendarSetResource; use crate::calendar_set::{CalendarSetResource, CalendarSetResourceService};
use crate::{CalDavPrincipalUri, Error}; use crate::{CalDavPrincipalUri, Error};
use async_trait::async_trait; use async_trait::async_trait;
use axum::Router;
use rustical_dav::extensions::{CommonPropertiesExtension, CommonPropertiesProp}; use rustical_dav::extensions::{CommonPropertiesExtension, CommonPropertiesProp};
use rustical_dav::privileges::UserPrivilegeSet; use rustical_dav::privileges::UserPrivilegeSet;
use rustical_dav::resource::{AxumMethods, PrincipalUri, Resource, ResourceService}; use rustical_dav::resource::{AxumMethods, PrincipalUri, Resource, ResourceService};
@@ -193,6 +194,29 @@ impl<AP: AuthenticationProvider, S: SubscriptionStore, CS: CalendarStore, BS: Ca
), ),
]) ])
} }
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> axum::Router<State> {
Router::new()
.nest(
"/calendar",
CalendarSetResourceService::new(
"calendar",
self.cal_store.clone(),
self.sub_store.clone(),
)
.axum_router(),
)
.nest(
"/birthdays",
CalendarSetResourceService::new(
"birthdays",
self.birthday_store.clone(),
self.sub_store.clone(),
)
.axum_router(),
)
.route_service("/", self.axum_service())
}
} }
impl<AP: AuthenticationProvider, S: SubscriptionStore, CS: CalendarStore, BS: CalendarStore> impl<AP: AuthenticationProvider, S: SubscriptionStore, CS: CalendarStore, BS: CalendarStore>

View File

@@ -1,9 +1,10 @@
use super::methods::mkcol::route_mkcol; use super::methods::mkcol::route_mkcol;
use super::methods::report::route_report_addressbook; use super::methods::report::route_report_addressbook;
use super::prop::{SupportedAddressData, SupportedReportSet}; use super::prop::{SupportedAddressData, SupportedReportSet};
use crate::address_object::resource::AddressObjectResource; use crate::address_object::resource::{AddressObjectResource, AddressObjectResourceService};
use crate::{CardDavPrincipalUri, Error}; use crate::{CardDavPrincipalUri, Error};
use async_trait::async_trait; use async_trait::async_trait;
use axum::Router;
use axum::extract::Request; use axum::extract::Request;
use axum::handler::Handler; use axum::handler::Handler;
use axum::response::Response; use axum::response::Response;
@@ -266,6 +267,15 @@ impl<AS: AddressbookStore, S: SubscriptionStore> ResourceService
.await?; .await?;
Ok(()) Ok(())
} }
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> Router<State> {
Router::new()
.nest(
"/{object_id}",
AddressObjectResourceService::new(self.addr_store.clone()).axum_router(),
)
.route_service("/", self.axum_service())
}
} }
impl<AS: AddressbookStore, S: SubscriptionStore> AxumMethods for AddressbookResourceService<AS, S> { impl<AS: AddressbookStore, S: SubscriptionStore> AxumMethods for AddressbookResourceService<AS, S> {

View File

@@ -1,5 +1,3 @@
use crate::address_object::resource::AddressObjectResourceService;
use crate::addressbook::resource::AddressbookResourceService;
use axum::{Extension, Router}; use axum::{Extension, Router};
use derive_more::Constructor; use derive_more::Constructor;
pub use error::Error; pub use error::Error;
@@ -38,22 +36,8 @@ pub fn carddav_router<AP: AuthenticationProvider, A: AddressbookStore, S: Subscr
auth_provider.clone(), auth_provider.clone(),
subscription_store.clone(), subscription_store.clone(),
); );
Router::new()
.route_service(
"/",
RootResourceService::<_, User, CardDavPrincipalUri>::new(principal_service.clone()) RootResourceService::<_, User, CardDavPrincipalUri>::new(principal_service.clone())
.axum_service(), .axum_router()
)
.route_service("/principal/{principal}", principal_service.axum_service())
.route_service(
"/principal/{principal}/{addressbook_id}",
AddressbookResourceService::new(store.clone(), subscription_store.clone())
.axum_service(),
)
.route_service(
"/principal/{principal}/{addressbook_id}/{object_id}",
AddressObjectResourceService::new(store.clone()).axum_service(),
)
.layer(AuthenticationLayer::new(auth_provider)) .layer(AuthenticationLayer::new(auth_provider))
.layer(Extension(CardDavPrincipalUri(prefix))) .layer(Extension(CardDavPrincipalUri(prefix)))
} }

View File

@@ -1,6 +1,7 @@
use crate::addressbook::resource::AddressbookResource; use crate::addressbook::resource::{AddressbookResource, AddressbookResourceService};
use crate::{CardDavPrincipalUri, Error}; use crate::{CardDavPrincipalUri, Error};
use async_trait::async_trait; use async_trait::async_trait;
use axum::Router;
use rustical_dav::extensions::{CommonPropertiesExtension, CommonPropertiesProp}; use rustical_dav::extensions::{CommonPropertiesExtension, CommonPropertiesProp};
use rustical_dav::privileges::UserPrivilegeSet; use rustical_dav::privileges::UserPrivilegeSet;
use rustical_dav::resource::{AxumMethods, PrincipalUri, Resource, ResourceService}; use rustical_dav::resource::{AxumMethods, PrincipalUri, Resource, ResourceService};
@@ -174,6 +175,16 @@ impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore> Reso
.map(|addressbook| (addressbook.id.to_owned(), addressbook.into())) .map(|addressbook| (addressbook.id.to_owned(), addressbook.into()))
.collect()) .collect())
} }
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> Router<State> {
Router::new()
.nest(
"/{addressbook_id}",
AddressbookResourceService::new(self.addr_store.clone(), self.sub_store.clone())
.axum_router(),
)
.route_service("/", self.axum_service())
}
} }
impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore> AxumMethods impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore> AxumMethods

View File

@@ -2,15 +2,18 @@ use super::{PrincipalUri, Resource};
use crate::Principal; use crate::Principal;
use crate::resource::{AxumMethods, AxumService}; use crate::resource::{AxumMethods, AxumService};
use async_trait::async_trait; use async_trait::async_trait;
use axum::Router;
use axum::extract::FromRequestParts;
use axum::response::IntoResponse;
use serde::Deserialize; use serde::Deserialize;
#[async_trait] #[async_trait]
pub trait ResourceService: Sized + Send + Sync + 'static { pub trait ResourceService: Clone + Sized + Send + Sync + AxumMethods + 'static {
type PathComponents: for<'de> Deserialize<'de> + Sized + Send + Sync + Clone + 'static; // defines how the resource URI maps to parameters, i.e. /{principal}/{calendar} -> (String, String) type PathComponents: for<'de> Deserialize<'de> + Sized + Send + Sync + Clone + 'static; // defines how the resource URI maps to parameters, i.e. /{principal}/{calendar} -> (String, String)
type MemberType: Resource<Error = Self::Error, Principal = Self::Principal>; type MemberType: Resource<Error = Self::Error, Principal = Self::Principal>;
type Resource: Resource<Error = Self::Error, Principal = Self::Principal>; type Resource: Resource<Error = Self::Error, Principal = Self::Principal>;
type Error: From<crate::Error> + Send; type Error: From<crate::Error> + Send + Sync + IntoResponse + 'static;
type Principal: Principal; type Principal: Principal + FromRequestParts<Self>;
type PrincipalUri: PrincipalUri; type PrincipalUri: PrincipalUri;
const DAV_HEADER: &'static str; const DAV_HEADER: &'static str;
@@ -45,8 +48,12 @@ pub trait ResourceService: Sized + Send + Sync + 'static {
fn axum_service(self) -> AxumService<Self> fn axum_service(self) -> AxumService<Self>
where where
Self: Clone + Send + Sync + AxumMethods, Self: AxumMethods,
{ {
AxumService::new(self) AxumService::new(self)
} }
fn axum_router<S: Send + Sync + Clone + 'static>(self) -> Router<S> {
Router::new().route_service("/", self.axum_service())
}
} }

View File

@@ -6,6 +6,8 @@ use crate::privileges::UserPrivilegeSet;
use crate::resource::{AxumMethods, PrincipalUri, Resource, ResourceService}; use crate::resource::{AxumMethods, PrincipalUri, Resource, ResourceService};
use crate::xml::{Resourcetype, ResourcetypeInner}; use crate::xml::{Resourcetype, ResourcetypeInner};
use async_trait::async_trait; use async_trait::async_trait;
use axum::Router;
use axum::extract::FromRequestParts;
use std::marker::PhantomData; use std::marker::PhantomData;
#[derive(Clone)] #[derive(Clone)]
@@ -59,8 +61,11 @@ impl<PRS: ResourceService + Clone, P: Principal, PURI: PrincipalUri>
} }
#[async_trait] #[async_trait]
impl<PRS: ResourceService<Principal = P> + Clone, P: Principal, PURI: PrincipalUri> ResourceService impl<
for RootResourceService<PRS, P, PURI> PRS: ResourceService<Principal = P> + Clone,
P: Principal + FromRequestParts<Self>,
PURI: PrincipalUri,
> ResourceService for RootResourceService<PRS, P, PURI>
{ {
type PathComponents = (); type PathComponents = ();
type MemberType = PRS::Resource; type MemberType = PRS::Resource;
@@ -74,6 +79,12 @@ impl<PRS: ResourceService<Principal = P> + Clone, P: Principal, PURI: PrincipalU
async fn get_resource(&self, _: &()) -> Result<Self::Resource, Self::Error> { async fn get_resource(&self, _: &()) -> Result<Self::Resource, Self::Error> {
Ok(RootResource::<PRS::Resource, P>::default()) Ok(RootResource::<PRS::Resource, P>::default())
} }
fn axum_router<S: Send + Sync + Clone + 'static>(self) -> Router<S> {
Router::new()
.nest("/principal/{principal}", self.0.clone().axum_router())
.route_service("/", self.axum_service())
}
} }
impl<PRS: ResourceService<Principal = P> + Clone, P: Principal, PURI: PrincipalUri> AxumMethods impl<PRS: ResourceService<Principal = P> + Clone, P: Principal, PURI: PrincipalUri> AxumMethods