2025-04-13 01:04:04 +02:00
|
|
|
use chrono::{DateTime, Utc};
|
2026-02-14 01:29:34 +01:00
|
|
|
use std::sync::Arc;
|
2026-03-07 14:59:32 +01:00
|
|
|
use uuid::Uuid;
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
use crate::application::dtos::calendar_dto::{
|
2026-02-14 01:29:34 +01:00
|
|
|
CalendarDto, CalendarEventDto, CreateCalendarDto, CreateEventDto, CreateEventICalDto,
|
|
|
|
|
UpdateCalendarDto, UpdateEventDto,
|
2025-04-13 01:04:04 +02:00
|
|
|
};
|
|
|
|
|
use crate::application::ports::calendar_ports::{CalendarStoragePort, CalendarUseCase};
|
|
|
|
|
use crate::common::errors::{DomainError, ErrorKind};
|
2026-03-03 15:36:42 +00:00
|
|
|
use crate::infrastructure::adapters::calendar_storage_adapter::CalendarStorageAdapter;
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
pub struct CalendarService {
|
2026-03-03 15:36:42 +00:00
|
|
|
calendar_storage: Arc<CalendarStorageAdapter>,
|
2025-04-13 01:04:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl CalendarService {
|
2026-03-03 15:36:42 +00:00
|
|
|
pub fn new(calendar_storage: Arc<CalendarStorageAdapter>) -> Self {
|
2026-02-14 01:29:34 +01:00
|
|
|
Self { calendar_storage }
|
2025-04-13 01:04:04 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl CalendarUseCase for CalendarService {
|
2026-02-14 01:29:34 +01:00
|
|
|
async fn create_calendar(
|
|
|
|
|
&self,
|
|
|
|
|
calendar: CreateCalendarDto,
|
2026-03-07 14:59:32 +01:00
|
|
|
user_id: Uuid,
|
2026-02-14 01:29:34 +01:00
|
|
|
) -> Result<CalendarDto, DomainError> {
|
|
|
|
|
self.calendar_storage
|
|
|
|
|
.create_calendar(calendar, user_id)
|
|
|
|
|
.await
|
2025-04-13 01:04:04 +02:00
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
|
|
|
|
|
async fn update_calendar(
|
|
|
|
|
&self,
|
|
|
|
|
calendar_id: &str,
|
|
|
|
|
update: UpdateCalendarDto,
|
2026-03-07 14:59:32 +01:00
|
|
|
user_id: Uuid,
|
2026-02-14 01:29:34 +01:00
|
|
|
) -> Result<CalendarDto, DomainError> {
|
|
|
|
|
let has_access = self
|
|
|
|
|
.calendar_storage
|
|
|
|
|
.check_calendar_access(calendar_id, user_id)
|
|
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
if !has_access {
|
|
|
|
|
return Err(DomainError::new(
|
|
|
|
|
ErrorKind::AccessDenied,
|
|
|
|
|
"Calendar",
|
2026-02-14 01:29:34 +01:00
|
|
|
"You don't have permission to update this calendar",
|
2025-04-13 01:04:04 +02:00
|
|
|
));
|
|
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
self.calendar_storage
|
|
|
|
|
.update_calendar(calendar_id, update)
|
|
|
|
|
.await
|
2025-04-13 01:04:04 +02:00
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2026-03-07 14:59:32 +01:00
|
|
|
async fn delete_calendar(&self, calendar_id: &str, user_id: Uuid) -> Result<(), DomainError> {
|
2026-02-14 01:29:34 +01:00
|
|
|
let has_access = self
|
|
|
|
|
.calendar_storage
|
|
|
|
|
.check_calendar_access(calendar_id, user_id)
|
|
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
if !has_access {
|
|
|
|
|
return Err(DomainError::new(
|
|
|
|
|
ErrorKind::AccessDenied,
|
|
|
|
|
"Calendar",
|
2026-02-14 01:29:34 +01:00
|
|
|
"You don't have permission to delete this calendar",
|
2025-04-13 01:04:04 +02:00
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
self.calendar_storage.delete_calendar(calendar_id).await
|
|
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2026-02-14 20:22:19 +01:00
|
|
|
async fn get_calendar(
|
|
|
|
|
&self,
|
|
|
|
|
calendar_id: &str,
|
2026-03-07 14:59:32 +01:00
|
|
|
user_id: Uuid,
|
2026-02-14 20:22:19 +01:00
|
|
|
) -> Result<CalendarDto, DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let calendar = self.calendar_storage.get_calendar(calendar_id).await?;
|
2026-02-14 01:29:34 +01:00
|
|
|
let has_access = self
|
|
|
|
|
.calendar_storage
|
|
|
|
|
.check_calendar_access(calendar_id, user_id)
|
|
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
if !has_access && !calendar.is_public {
|
|
|
|
|
return Err(DomainError::new(
|
|
|
|
|
ErrorKind::AccessDenied,
|
|
|
|
|
"Calendar",
|
2026-02-14 01:29:34 +01:00
|
|
|
"You don't have permission to view this calendar",
|
2025-04-13 01:04:04 +02:00
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
Ok(calendar)
|
|
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2026-03-07 14:59:32 +01:00
|
|
|
async fn list_my_calendars(&self, user_id: Uuid) -> Result<Vec<CalendarDto>, DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
self.calendar_storage.list_calendars_by_owner(user_id).await
|
|
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2026-03-07 14:59:32 +01:00
|
|
|
async fn list_shared_calendars(&self, user_id: Uuid) -> Result<Vec<CalendarDto>, DomainError> {
|
2026-02-14 01:29:34 +01:00
|
|
|
self.calendar_storage
|
|
|
|
|
.list_calendars_shared_with_user(user_id)
|
|
|
|
|
.await
|
2025-04-13 01:04:04 +02:00
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
|
|
|
|
|
async fn list_public_calendars(
|
|
|
|
|
&self,
|
|
|
|
|
limit: Option<i64>,
|
|
|
|
|
offset: Option<i64>,
|
|
|
|
|
) -> Result<Vec<CalendarDto>, DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let limit = limit.unwrap_or(100);
|
|
|
|
|
let offset = offset.unwrap_or(0);
|
2026-02-14 01:29:34 +01:00
|
|
|
self.calendar_storage
|
|
|
|
|
.list_public_calendars(limit, offset)
|
|
|
|
|
.await
|
2025-04-13 01:04:04 +02:00
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
|
|
|
|
|
async fn share_calendar(
|
|
|
|
|
&self,
|
|
|
|
|
calendar_id: &str,
|
2026-03-07 14:59:32 +01:00
|
|
|
target_user_id: Uuid,
|
2026-02-14 01:29:34 +01:00
|
|
|
access_level: &str,
|
2026-03-07 14:59:32 +01:00
|
|
|
caller_user_id: Uuid,
|
2026-02-14 01:29:34 +01:00
|
|
|
) -> Result<(), DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let calendar = self.calendar_storage.get_calendar(calendar_id).await?;
|
2026-03-07 14:59:32 +01:00
|
|
|
if calendar.owner_id != caller_user_id.to_string() {
|
2025-04-13 01:04:04 +02:00
|
|
|
return Err(DomainError::new(
|
|
|
|
|
ErrorKind::AccessDenied,
|
|
|
|
|
"Calendar",
|
2026-02-14 01:29:34 +01:00
|
|
|
"Only the calendar owner can change sharing settings",
|
2025-04-13 01:04:04 +02:00
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
match access_level {
|
2026-02-14 01:29:34 +01:00
|
|
|
"read" | "write" | "owner" => {}
|
|
|
|
|
_ => {
|
|
|
|
|
return Err(DomainError::new(
|
|
|
|
|
ErrorKind::InvalidInput,
|
|
|
|
|
"Calendar",
|
|
|
|
|
format!(
|
|
|
|
|
"Invalid access level: {}. Valid values are: read, write, owner",
|
|
|
|
|
access_level
|
|
|
|
|
),
|
|
|
|
|
));
|
|
|
|
|
}
|
2025-04-13 01:04:04 +02:00
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
self.calendar_storage
|
2026-02-14 20:22:19 +01:00
|
|
|
.share_calendar(calendar_id, target_user_id, access_level)
|
2026-02-14 01:29:34 +01:00
|
|
|
.await
|
2025-04-13 01:04:04 +02:00
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
|
|
|
|
|
async fn remove_calendar_sharing(
|
|
|
|
|
&self,
|
|
|
|
|
calendar_id: &str,
|
2026-03-07 14:59:32 +01:00
|
|
|
target_user_id: Uuid,
|
|
|
|
|
caller_user_id: Uuid,
|
2026-02-14 01:29:34 +01:00
|
|
|
) -> Result<(), DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let calendar = self.calendar_storage.get_calendar(calendar_id).await?;
|
2026-03-07 14:59:32 +01:00
|
|
|
if calendar.owner_id != caller_user_id.to_string() {
|
2025-04-13 01:04:04 +02:00
|
|
|
return Err(DomainError::new(
|
|
|
|
|
ErrorKind::AccessDenied,
|
|
|
|
|
"Calendar",
|
2026-02-14 01:29:34 +01:00
|
|
|
"Only the calendar owner can change sharing settings",
|
2025-04-13 01:04:04 +02:00
|
|
|
));
|
|
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
self.calendar_storage
|
2026-02-14 20:22:19 +01:00
|
|
|
.remove_calendar_sharing(calendar_id, target_user_id)
|
2026-02-14 01:29:34 +01:00
|
|
|
.await
|
2025-04-13 01:04:04 +02:00
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
|
|
|
|
|
async fn get_calendar_shares(
|
|
|
|
|
&self,
|
|
|
|
|
calendar_id: &str,
|
2026-03-07 14:59:32 +01:00
|
|
|
user_id: Uuid,
|
2026-02-14 01:29:34 +01:00
|
|
|
) -> Result<Vec<(String, String)>, DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let calendar = self.calendar_storage.get_calendar(calendar_id).await?;
|
2026-03-07 14:59:32 +01:00
|
|
|
if calendar.owner_id != user_id.to_string() {
|
2025-04-13 01:04:04 +02:00
|
|
|
return Err(DomainError::new(
|
|
|
|
|
ErrorKind::AccessDenied,
|
|
|
|
|
"Calendar",
|
2026-02-14 01:29:34 +01:00
|
|
|
"Only the calendar owner can view sharing settings",
|
2025-04-13 01:04:04 +02:00
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
self.calendar_storage.get_calendar_shares(calendar_id).await
|
|
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2026-02-14 20:22:19 +01:00
|
|
|
async fn create_event(
|
|
|
|
|
&self,
|
|
|
|
|
event: CreateEventDto,
|
2026-03-07 14:59:32 +01:00
|
|
|
user_id: Uuid,
|
2026-02-14 20:22:19 +01:00
|
|
|
) -> Result<CalendarEventDto, DomainError> {
|
2026-02-14 01:29:34 +01:00
|
|
|
let has_access = self
|
|
|
|
|
.calendar_storage
|
|
|
|
|
.check_calendar_access(&event.calendar_id, user_id)
|
|
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
if !has_access {
|
|
|
|
|
return Err(DomainError::new(
|
|
|
|
|
ErrorKind::AccessDenied,
|
|
|
|
|
"Calendar",
|
2026-02-14 01:29:34 +01:00
|
|
|
"You don't have permission to add events to this calendar",
|
2025-04-13 01:04:04 +02:00
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
self.calendar_storage.create_event(event).await
|
|
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
|
|
|
|
|
async fn create_event_from_ical(
|
|
|
|
|
&self,
|
|
|
|
|
event: CreateEventICalDto,
|
2026-03-07 14:59:32 +01:00
|
|
|
user_id: Uuid,
|
2026-02-14 01:29:34 +01:00
|
|
|
) -> Result<CalendarEventDto, DomainError> {
|
|
|
|
|
let has_access = self
|
|
|
|
|
.calendar_storage
|
|
|
|
|
.check_calendar_access(&event.calendar_id, user_id)
|
|
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
if !has_access {
|
|
|
|
|
return Err(DomainError::new(
|
|
|
|
|
ErrorKind::AccessDenied,
|
|
|
|
|
"Calendar",
|
2026-02-14 01:29:34 +01:00
|
|
|
"You don't have permission to add events to this calendar",
|
2025-04-13 01:04:04 +02:00
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
self.calendar_storage.create_event_from_ical(event).await
|
|
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
|
|
|
|
|
async fn update_event(
|
|
|
|
|
&self,
|
|
|
|
|
event_id: &str,
|
|
|
|
|
update: UpdateEventDto,
|
2026-03-07 14:59:32 +01:00
|
|
|
user_id: Uuid,
|
2026-02-14 01:29:34 +01:00
|
|
|
) -> Result<CalendarEventDto, DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let event = self.calendar_storage.get_event(event_id).await?;
|
2026-02-14 01:29:34 +01:00
|
|
|
let has_access = self
|
|
|
|
|
.calendar_storage
|
|
|
|
|
.check_calendar_access(&event.calendar_id, user_id)
|
|
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
if !has_access {
|
|
|
|
|
return Err(DomainError::new(
|
|
|
|
|
ErrorKind::AccessDenied,
|
|
|
|
|
"Calendar",
|
2026-02-14 01:29:34 +01:00
|
|
|
"You don't have permission to update events in this calendar",
|
2025-04-13 01:04:04 +02:00
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
self.calendar_storage.update_event(event_id, update).await
|
|
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2026-03-07 14:59:32 +01:00
|
|
|
async fn delete_event(&self, event_id: &str, user_id: Uuid) -> Result<(), DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let event = self.calendar_storage.get_event(event_id).await?;
|
2026-02-14 01:29:34 +01:00
|
|
|
let has_access = self
|
|
|
|
|
.calendar_storage
|
|
|
|
|
.check_calendar_access(&event.calendar_id, user_id)
|
|
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
if !has_access {
|
|
|
|
|
return Err(DomainError::new(
|
|
|
|
|
ErrorKind::AccessDenied,
|
|
|
|
|
"Calendar",
|
2026-02-14 01:29:34 +01:00
|
|
|
"You don't have permission to delete events in this calendar",
|
2025-04-13 01:04:04 +02:00
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
self.calendar_storage.delete_event(event_id).await
|
|
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2026-02-14 20:22:19 +01:00
|
|
|
async fn get_event(
|
|
|
|
|
&self,
|
|
|
|
|
event_id: &str,
|
2026-03-07 14:59:32 +01:00
|
|
|
user_id: Uuid,
|
2026-02-14 20:22:19 +01:00
|
|
|
) -> Result<CalendarEventDto, DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let event = self.calendar_storage.get_event(event_id).await?;
|
2026-02-14 01:29:34 +01:00
|
|
|
let has_access = self
|
|
|
|
|
.calendar_storage
|
|
|
|
|
.check_calendar_access(&event.calendar_id, user_id)
|
|
|
|
|
.await?;
|
|
|
|
|
let calendar = self
|
|
|
|
|
.calendar_storage
|
|
|
|
|
.get_calendar(&event.calendar_id)
|
|
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
if !has_access && !calendar.is_public {
|
|
|
|
|
return Err(DomainError::new(
|
|
|
|
|
ErrorKind::AccessDenied,
|
|
|
|
|
"Calendar",
|
2026-02-14 01:29:34 +01:00
|
|
|
"You don't have permission to view events in this calendar",
|
2025-04-13 01:04:04 +02:00
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
Ok(event)
|
|
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2026-06-10 09:52:28 +00:00
|
|
|
async fn get_event_by_ical_uid(
|
|
|
|
|
&self,
|
|
|
|
|
calendar_id: &str,
|
|
|
|
|
ical_uid: &str,
|
|
|
|
|
user_id: Uuid,
|
|
|
|
|
) -> Result<Option<CalendarEventDto>, DomainError> {
|
|
|
|
|
let has_access = self
|
|
|
|
|
.calendar_storage
|
|
|
|
|
.check_calendar_access(calendar_id, user_id)
|
|
|
|
|
.await?;
|
|
|
|
|
let calendar = self.calendar_storage.get_calendar(calendar_id).await?;
|
|
|
|
|
if !has_access && !calendar.is_public {
|
|
|
|
|
return Err(DomainError::new(
|
|
|
|
|
ErrorKind::AccessDenied,
|
|
|
|
|
"Calendar",
|
|
|
|
|
"You don't have permission to view events in this calendar",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
self.calendar_storage
|
|
|
|
|
.find_event_by_ical_uid(calendar_id, ical_uid)
|
|
|
|
|
.await
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
async fn list_events(
|
|
|
|
|
&self,
|
|
|
|
|
calendar_id: &str,
|
|
|
|
|
limit: Option<i64>,
|
|
|
|
|
offset: Option<i64>,
|
2026-03-07 14:59:32 +01:00
|
|
|
user_id: Uuid,
|
2026-02-14 01:29:34 +01:00
|
|
|
) -> Result<Vec<CalendarEventDto>, DomainError> {
|
|
|
|
|
let has_access = self
|
|
|
|
|
.calendar_storage
|
|
|
|
|
.check_calendar_access(calendar_id, user_id)
|
|
|
|
|
.await?;
|
2026-02-10 18:46:59 +01:00
|
|
|
let calendar = self.calendar_storage.get_calendar(calendar_id).await?;
|
|
|
|
|
if !has_access && !calendar.is_public {
|
2026-02-14 01:29:34 +01:00
|
|
|
return Err(DomainError::new(
|
|
|
|
|
ErrorKind::AccessDenied,
|
|
|
|
|
"Calendar",
|
|
|
|
|
"You don't have permission to view events in this calendar",
|
|
|
|
|
));
|
2026-02-10 18:46:59 +01:00
|
|
|
}
|
|
|
|
|
if limit.is_some() || offset.is_some() {
|
|
|
|
|
let limit = limit.unwrap_or(100);
|
|
|
|
|
let offset = offset.unwrap_or(0);
|
2026-02-14 01:29:34 +01:00
|
|
|
self.calendar_storage
|
|
|
|
|
.list_events_by_calendar_paginated(calendar_id, limit, offset)
|
|
|
|
|
.await
|
2026-02-10 18:46:59 +01:00
|
|
|
} else {
|
2026-02-14 01:29:34 +01:00
|
|
|
self.calendar_storage
|
|
|
|
|
.list_events_by_calendar(calendar_id)
|
|
|
|
|
.await
|
2026-02-10 18:46:59 +01:00
|
|
|
}
|
|
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2026-02-14 20:22:19 +01:00
|
|
|
async fn get_events_in_range(
|
2026-02-14 01:29:34 +01:00
|
|
|
&self,
|
|
|
|
|
calendar_id: &str,
|
|
|
|
|
start: DateTime<Utc>,
|
|
|
|
|
end: DateTime<Utc>,
|
2026-03-07 14:59:32 +01:00
|
|
|
user_id: Uuid,
|
2026-02-14 01:29:34 +01:00
|
|
|
) -> Result<Vec<CalendarEventDto>, DomainError> {
|
|
|
|
|
let has_access = self
|
|
|
|
|
.calendar_storage
|
|
|
|
|
.check_calendar_access(calendar_id, user_id)
|
|
|
|
|
.await?;
|
2026-02-10 18:46:59 +01:00
|
|
|
let calendar = self.calendar_storage.get_calendar(calendar_id).await?;
|
|
|
|
|
if !has_access && !calendar.is_public {
|
2026-02-14 01:29:34 +01:00
|
|
|
return Err(DomainError::new(
|
|
|
|
|
ErrorKind::AccessDenied,
|
|
|
|
|
"Calendar",
|
|
|
|
|
"You don't have permission to view events in this calendar",
|
|
|
|
|
));
|
2026-02-10 18:46:59 +01:00
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
self.calendar_storage
|
|
|
|
|
.get_events_in_time_range(calendar_id, &start, &end)
|
|
|
|
|
.await
|
2026-02-10 18:46:59 +01:00
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
}
|