2025-04-13 01:04:04 +02:00
|
|
|
use chrono::Utc;
|
|
|
|
|
use std::sync::Arc;
|
2026-03-09 14:34:07 +01:00
|
|
|
use uuid::Uuid;
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
use crate::application::dtos::address_book_dto::{
|
2026-02-14 01:29:34 +01:00
|
|
|
AddressBookDto, CreateAddressBookDto, ShareAddressBookDto, UnshareAddressBookDto,
|
|
|
|
|
UpdateAddressBookDto,
|
2025-04-13 01:04:04 +02:00
|
|
|
};
|
|
|
|
|
use crate::application::dtos::contact_dto::{
|
2026-02-14 01:29:34 +01:00
|
|
|
ContactDto, ContactGroupDto, CreateContactDto, CreateContactGroupDto, CreateContactVCardDto,
|
|
|
|
|
GroupMembershipDto, UpdateContactDto, UpdateContactGroupDto,
|
2025-04-13 01:04:04 +02:00
|
|
|
};
|
|
|
|
|
use crate::application::ports::carddav_ports::{AddressBookUseCase, ContactUseCase};
|
|
|
|
|
use crate::application::ports::storage_ports::StorageUseCase;
|
2026-02-02 23:56:40 +01:00
|
|
|
use crate::common::errors::DomainError;
|
2026-02-14 01:29:34 +01:00
|
|
|
use crate::domain::entities::contact::{Address, AddressBook, Contact, ContactGroup, Email, Phone};
|
2025-04-13 01:04:04 +02:00
|
|
|
use crate::domain::repositories::address_book_repository::AddressBookRepository;
|
2026-02-14 01:29:34 +01:00
|
|
|
use crate::domain::repositories::contact_repository::{ContactGroupRepository, ContactRepository};
|
2026-03-03 15:36:42 +00:00
|
|
|
use crate::infrastructure::repositories::pg::AddressBookPgRepository;
|
|
|
|
|
use crate::infrastructure::repositories::pg::ContactGroupPgRepository;
|
|
|
|
|
use crate::infrastructure::repositories::pg::ContactPgRepository;
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
pub struct ContactService {
|
2026-03-03 15:36:42 +00:00
|
|
|
address_book_repository: Arc<AddressBookPgRepository>,
|
|
|
|
|
contact_repository: Arc<ContactPgRepository>,
|
|
|
|
|
contact_group_repository: Arc<ContactGroupPgRepository>,
|
2025-04-13 01:04:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ContactService {
|
|
|
|
|
pub fn new(
|
2026-03-03 15:36:42 +00:00
|
|
|
address_book_repository: Arc<AddressBookPgRepository>,
|
|
|
|
|
contact_repository: Arc<ContactPgRepository>,
|
|
|
|
|
contact_group_repository: Arc<ContactGroupPgRepository>,
|
2025-04-13 01:04:04 +02:00
|
|
|
) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
address_book_repository,
|
|
|
|
|
contact_repository,
|
|
|
|
|
contact_group_repository,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Helper methods
|
2026-02-14 01:29:34 +01:00
|
|
|
async fn check_address_book_access(
|
|
|
|
|
&self,
|
|
|
|
|
address_book_id: &Uuid,
|
2026-03-07 14:59:32 +01:00
|
|
|
user_id: &Uuid,
|
2026-02-14 01:29:34 +01:00
|
|
|
) -> Result<AddressBook, DomainError> {
|
|
|
|
|
let address_book = self
|
|
|
|
|
.address_book_repository
|
|
|
|
|
.get_address_book_by_id(address_book_id)
|
2025-04-13 01:04:04 +02:00
|
|
|
.await?
|
|
|
|
|
.ok_or_else(|| DomainError::not_found("Address book", "not found"))?;
|
|
|
|
|
|
|
|
|
|
// Check if user is owner
|
2026-03-07 14:59:32 +01:00
|
|
|
if address_book.owner_id() == user_id.to_string() {
|
2025-04-13 01:04:04 +02:00
|
|
|
return Ok(address_book);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if address book is shared with user
|
2026-02-14 01:29:34 +01:00
|
|
|
let shares = self
|
|
|
|
|
.address_book_repository
|
|
|
|
|
.get_address_book_shares(address_book_id)
|
|
|
|
|
.await?;
|
2026-03-07 14:59:32 +01:00
|
|
|
if shares.iter().any(|(id, _)| id == &user_id.to_string()) {
|
2025-04-13 01:04:04 +02:00
|
|
|
return Ok(address_book);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if address book is public
|
2026-02-08 13:40:23 +01:00
|
|
|
if address_book.is_public() {
|
2025-04-13 01:04:04 +02:00
|
|
|
return Ok(address_book);
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
Err(DomainError::unauthorized(
|
|
|
|
|
"You don't have access to this address book",
|
|
|
|
|
))
|
2025-04-13 01:04:04 +02:00
|
|
|
}
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
async fn check_address_book_write_access(
|
|
|
|
|
&self,
|
|
|
|
|
address_book_id: &Uuid,
|
2026-03-07 14:59:32 +01:00
|
|
|
user_id: &Uuid,
|
2026-02-14 01:29:34 +01:00
|
|
|
) -> Result<AddressBook, DomainError> {
|
|
|
|
|
let address_book = self
|
|
|
|
|
.address_book_repository
|
|
|
|
|
.get_address_book_by_id(address_book_id)
|
2025-04-13 01:04:04 +02:00
|
|
|
.await?
|
|
|
|
|
.ok_or_else(|| DomainError::not_found("Address book", "not found"))?;
|
|
|
|
|
|
|
|
|
|
// Check if user is owner
|
2026-03-07 14:59:32 +01:00
|
|
|
if address_book.owner_id() == user_id.to_string() {
|
2025-04-13 01:04:04 +02:00
|
|
|
return Ok(address_book);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if address book is shared with user with write access
|
2026-02-14 01:29:34 +01:00
|
|
|
let shares = self
|
|
|
|
|
.address_book_repository
|
|
|
|
|
.get_address_book_shares(address_book_id)
|
|
|
|
|
.await?;
|
|
|
|
|
if shares
|
|
|
|
|
.iter()
|
2026-03-07 14:59:32 +01:00
|
|
|
.any(|(id, can_write)| id == &user_id.to_string() && *can_write)
|
2026-02-14 01:29:34 +01:00
|
|
|
{
|
2025-04-13 01:04:04 +02:00
|
|
|
return Ok(address_book);
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
Err(DomainError::unauthorized(
|
|
|
|
|
"You don't have write access to this address book",
|
|
|
|
|
))
|
2025-04-13 01:04:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_vcard(&self, vcard_data: &str) -> Result<Contact, DomainError> {
|
|
|
|
|
// This is a simplified vCard parser - a real implementation would use a proper vCard library
|
|
|
|
|
// For now, we'll create a basic contact with minimal data
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
let mut contact = Contact::default();
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
let lines: Vec<&str> = vcard_data.lines().collect();
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2026-02-15 17:53:25 +01:00
|
|
|
for line in &lines {
|
|
|
|
|
let line = line.trim();
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2026-02-15 17:53:25 +01:00
|
|
|
if let Some(stripped) = line.strip_prefix("FN:") {
|
|
|
|
|
contact.set_full_name(Some(stripped.to_string()));
|
|
|
|
|
} else if let Some(stripped) = line.strip_prefix("N:") {
|
|
|
|
|
let parts: Vec<&str> = stripped.split(';').collect();
|
2025-04-13 01:04:04 +02:00
|
|
|
if parts.len() >= 2 {
|
2026-02-08 13:40:23 +01:00
|
|
|
contact.set_last_name(Some(parts[0].to_string()));
|
|
|
|
|
contact.set_first_name(Some(parts[1].to_string()));
|
2025-04-13 01:04:04 +02:00
|
|
|
}
|
|
|
|
|
} else if line.starts_with("EMAIL") {
|
|
|
|
|
let value = line.split(':').nth(1).unwrap_or("");
|
|
|
|
|
if !value.is_empty() {
|
|
|
|
|
let email_type = if line.contains("TYPE=HOME") {
|
|
|
|
|
"home"
|
|
|
|
|
} else if line.contains("TYPE=WORK") {
|
|
|
|
|
"work"
|
|
|
|
|
} else {
|
|
|
|
|
"other"
|
|
|
|
|
};
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2026-02-08 13:40:23 +01:00
|
|
|
contact.push_email(Email {
|
2025-04-13 01:04:04 +02:00
|
|
|
email: value.to_string(),
|
|
|
|
|
r#type: email_type.to_string(),
|
2026-02-08 13:40:23 +01:00
|
|
|
is_primary: contact.email_is_empty(), // First one is primary
|
2025-04-13 01:04:04 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
} else if line.starts_with("TEL") {
|
|
|
|
|
let value = line.split(':').nth(1).unwrap_or("");
|
|
|
|
|
if !value.is_empty() {
|
|
|
|
|
let phone_type = if line.contains("TYPE=CELL") || line.contains("TYPE=MOBILE") {
|
|
|
|
|
"mobile"
|
|
|
|
|
} else if line.contains("TYPE=HOME") {
|
|
|
|
|
"home"
|
|
|
|
|
} else if line.contains("TYPE=WORK") {
|
|
|
|
|
"work"
|
|
|
|
|
} else if line.contains("TYPE=FAX") {
|
|
|
|
|
"fax"
|
|
|
|
|
} else {
|
|
|
|
|
"other"
|
|
|
|
|
};
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2026-02-08 13:40:23 +01:00
|
|
|
contact.push_phone(Phone {
|
2025-04-13 01:04:04 +02:00
|
|
|
number: value.to_string(),
|
|
|
|
|
r#type: phone_type.to_string(),
|
2026-02-08 13:40:23 +01:00
|
|
|
is_primary: contact.phone_is_empty(), // First one is primary
|
2025-04-13 01:04:04 +02:00
|
|
|
});
|
|
|
|
|
}
|
2026-02-15 17:53:25 +01:00
|
|
|
} else if let Some(stripped) = line.strip_prefix("ORG:") {
|
|
|
|
|
contact.set_organization(Some(stripped.to_string()));
|
|
|
|
|
} else if let Some(stripped) = line.strip_prefix("TITLE:") {
|
|
|
|
|
contact.set_title(Some(stripped.to_string()));
|
|
|
|
|
} else if let Some(stripped) = line.strip_prefix("NOTE:") {
|
|
|
|
|
contact.set_notes(Some(stripped.to_string()));
|
|
|
|
|
} else if let Some(stripped) = line.strip_prefix("UID:") {
|
|
|
|
|
contact.set_uid(stripped.to_string());
|
2025-04-13 01:04:04 +02:00
|
|
|
}
|
|
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
// Store the original vCard data
|
2026-02-08 13:40:23 +01:00
|
|
|
contact.set_vcard(vcard_data.to_string());
|
|
|
|
|
contact.set_etag(Uuid::new_v4().to_string());
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
Ok(contact)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn generate_vcard(&self, contact: &Contact) -> String {
|
|
|
|
|
let mut vcard = String::from("BEGIN:VCARD\r\nVERSION:3.0\r\n");
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
// UID
|
2026-02-08 13:40:23 +01:00
|
|
|
vcard.push_str(&format!("UID:{}\r\n", contact.uid()));
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
// Name fields
|
2026-02-08 13:40:23 +01:00
|
|
|
if let Some(full_name) = contact.full_name() {
|
2025-04-13 01:04:04 +02:00
|
|
|
vcard.push_str(&format!("FN:{}\r\n", full_name));
|
|
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2026-02-08 13:40:23 +01:00
|
|
|
let last_name = contact.last_name().unwrap_or_default().to_string();
|
|
|
|
|
let first_name = contact.first_name().unwrap_or_default().to_string();
|
2025-04-13 01:04:04 +02:00
|
|
|
vcard.push_str(&format!("N:{};{};;;\r\n", last_name, first_name));
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
// Email addresses
|
2026-02-08 13:40:23 +01:00
|
|
|
for email in contact.email() {
|
2026-02-14 01:29:34 +01:00
|
|
|
vcard.push_str(&format!(
|
|
|
|
|
"EMAIL;TYPE={}:{}\r\n",
|
2025-04-13 01:04:04 +02:00
|
|
|
email.r#type.to_uppercase(),
|
2026-02-14 01:29:34 +01:00
|
|
|
email.email
|
|
|
|
|
));
|
2025-04-13 01:04:04 +02:00
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
// Phone numbers
|
2026-02-08 13:40:23 +01:00
|
|
|
for phone in contact.phone() {
|
2025-04-13 01:04:04 +02:00
|
|
|
let tel_type = match phone.r#type.as_str() {
|
|
|
|
|
"mobile" => "CELL",
|
|
|
|
|
"home" => "HOME",
|
|
|
|
|
"work" => "WORK",
|
|
|
|
|
"fax" => "FAX",
|
|
|
|
|
_ => "OTHER",
|
|
|
|
|
};
|
|
|
|
|
vcard.push_str(&format!("TEL;TYPE={}:{}\r\n", tel_type, phone.number));
|
|
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
// Addresses
|
2026-02-08 13:40:23 +01:00
|
|
|
for addr in contact.address() {
|
2025-04-13 01:04:04 +02:00
|
|
|
let addr_type = addr.r#type.to_uppercase();
|
|
|
|
|
let street = addr.street.clone().unwrap_or_default();
|
|
|
|
|
let city = addr.city.clone().unwrap_or_default();
|
|
|
|
|
let state = addr.state.clone().unwrap_or_default();
|
|
|
|
|
let postal_code = addr.postal_code.clone().unwrap_or_default();
|
|
|
|
|
let country = addr.country.clone().unwrap_or_default();
|
2026-02-14 01:29:34 +01:00
|
|
|
|
|
|
|
|
vcard.push_str(&format!(
|
|
|
|
|
"ADR;TYPE={}:;;{};{};{};{};{}\r\n",
|
|
|
|
|
addr_type, street, city, state, postal_code, country
|
|
|
|
|
));
|
2025-04-13 01:04:04 +02:00
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
// Organization
|
2026-02-08 13:40:23 +01:00
|
|
|
if let Some(org) = contact.organization() {
|
2025-04-13 01:04:04 +02:00
|
|
|
vcard.push_str(&format!("ORG:{}\r\n", org));
|
|
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
// Title
|
2026-02-08 13:40:23 +01:00
|
|
|
if let Some(title) = contact.title() {
|
2025-04-13 01:04:04 +02:00
|
|
|
vcard.push_str(&format!("TITLE:{}\r\n", title));
|
|
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
// Notes
|
2026-02-08 13:40:23 +01:00
|
|
|
if let Some(notes) = contact.notes() {
|
2025-04-13 01:04:04 +02:00
|
|
|
vcard.push_str(&format!("NOTE:{}\r\n", notes));
|
|
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
// Birthday
|
2026-02-08 13:40:23 +01:00
|
|
|
if let Some(birthday) = contact.birthday() {
|
2025-04-13 01:04:04 +02:00
|
|
|
vcard.push_str(&format!("BDAY:{}\r\n", birthday.format("%Y%m%d")));
|
|
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
// Revision (last update)
|
2026-02-14 01:29:34 +01:00
|
|
|
vcard.push_str(&format!(
|
|
|
|
|
"REV:{}\r\n",
|
|
|
|
|
contact.updated_at().format("%Y%m%dT%H%M%SZ")
|
|
|
|
|
));
|
|
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
vcard.push_str("END:VCARD\r\n");
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
vcard
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl AddressBookUseCase for ContactService {
|
2026-02-14 01:29:34 +01:00
|
|
|
async fn create_address_book(
|
|
|
|
|
&self,
|
|
|
|
|
dto: CreateAddressBookDto,
|
|
|
|
|
) -> Result<AddressBookDto, DomainError> {
|
2026-02-08 13:40:23 +01:00
|
|
|
let address_book = AddressBook::new(
|
|
|
|
|
dto.name,
|
|
|
|
|
dto.owner_id,
|
|
|
|
|
dto.description,
|
|
|
|
|
dto.color,
|
|
|
|
|
dto.is_public.unwrap_or(false),
|
|
|
|
|
);
|
2025-04-13 01:04:04 +02:00
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
let created_address_book = self
|
|
|
|
|
.address_book_repository
|
|
|
|
|
.create_address_book(address_book)
|
|
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
Ok(AddressBookDto::from(created_address_book))
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
async fn update_address_book(
|
|
|
|
|
&self,
|
|
|
|
|
address_book_id: &str,
|
|
|
|
|
update: UpdateAddressBookDto,
|
|
|
|
|
) -> Result<AddressBookDto, DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let id = Uuid::parse_str(address_book_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid address book ID format"))?;
|
|
|
|
|
|
|
|
|
|
// Check if user has write access to the address book
|
2026-02-14 01:29:34 +01:00
|
|
|
let address_book = self
|
2026-03-09 14:34:07 +01:00
|
|
|
.check_address_book_write_access(
|
|
|
|
|
&id,
|
|
|
|
|
&Uuid::parse_str(&update.user_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid user ID format"))?,
|
|
|
|
|
)
|
2026-02-14 01:29:34 +01:00
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
// Apply updates
|
2026-02-08 13:40:23 +01:00
|
|
|
let updated_address_book = AddressBook::from_raw(
|
2025-04-13 01:04:04 +02:00
|
|
|
id,
|
2026-02-14 01:29:34 +01:00
|
|
|
update
|
|
|
|
|
.name
|
|
|
|
|
.unwrap_or_else(|| address_book.name().to_string()),
|
2026-02-08 13:40:23 +01:00
|
|
|
address_book.owner_id().to_string(),
|
2026-02-14 01:29:34 +01:00
|
|
|
update
|
|
|
|
|
.description
|
|
|
|
|
.or_else(|| address_book.description().map(|s| s.to_string())),
|
|
|
|
|
update
|
|
|
|
|
.color
|
|
|
|
|
.or_else(|| address_book.color().map(|s| s.to_string())),
|
2026-02-08 13:40:23 +01:00
|
|
|
update.is_public.unwrap_or(address_book.is_public()),
|
|
|
|
|
*address_book.created_at(),
|
|
|
|
|
Utc::now(),
|
|
|
|
|
);
|
2025-04-13 01:04:04 +02:00
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
let result = self
|
|
|
|
|
.address_book_repository
|
|
|
|
|
.update_address_book(updated_address_book)
|
|
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
Ok(AddressBookDto::from(result))
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
async fn delete_address_book(
|
|
|
|
|
&self,
|
|
|
|
|
address_book_id: &str,
|
2026-03-07 14:59:32 +01:00
|
|
|
user_id: Uuid,
|
2026-02-14 01:29:34 +01:00
|
|
|
) -> Result<(), DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let id = Uuid::parse_str(address_book_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid address book ID format"))?;
|
|
|
|
|
|
|
|
|
|
// Verify that the user is the owner of the address book
|
2026-02-14 01:29:34 +01:00
|
|
|
let address_book = self
|
|
|
|
|
.address_book_repository
|
|
|
|
|
.get_address_book_by_id(&id)
|
2025-04-13 01:04:04 +02:00
|
|
|
.await?
|
|
|
|
|
.ok_or_else(|| DomainError::not_found("Address book", "not found"))?;
|
|
|
|
|
|
2026-03-07 14:59:32 +01:00
|
|
|
if address_book.owner_id() != user_id.to_string() {
|
2026-02-14 01:29:34 +01:00
|
|
|
return Err(DomainError::unauthorized(
|
|
|
|
|
"Only the owner can delete an address book",
|
|
|
|
|
));
|
2025-04-13 01:04:04 +02:00
|
|
|
}
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
self.address_book_repository
|
|
|
|
|
.delete_address_book(&id)
|
|
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
async fn get_address_book(
|
|
|
|
|
&self,
|
|
|
|
|
address_book_id: &str,
|
2026-03-07 14:59:32 +01:00
|
|
|
user_id: Uuid,
|
2026-02-14 01:29:34 +01:00
|
|
|
) -> Result<AddressBookDto, DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let id = Uuid::parse_str(address_book_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid address book ID format"))?;
|
|
|
|
|
|
2026-03-07 14:59:32 +01:00
|
|
|
let address_book = self.check_address_book_access(&id, &user_id).await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
Ok(AddressBookDto::from(address_book))
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
async fn list_user_address_books(
|
|
|
|
|
&self,
|
2026-03-07 14:59:32 +01:00
|
|
|
user_id: Uuid,
|
2026-02-14 01:29:34 +01:00
|
|
|
) -> Result<Vec<AddressBookDto>, DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
// Get address books owned by the user
|
2026-02-14 01:29:34 +01:00
|
|
|
let owned_address_books = self
|
|
|
|
|
.address_book_repository
|
|
|
|
|
.get_address_books_by_owner(user_id)
|
|
|
|
|
.await?;
|
|
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
// Get address books shared with the user
|
2026-02-14 01:29:34 +01:00
|
|
|
let shared_address_books = self
|
|
|
|
|
.address_book_repository
|
|
|
|
|
.get_shared_address_books(user_id)
|
|
|
|
|
.await?;
|
|
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
// Get public address books
|
2026-02-14 01:29:34 +01:00
|
|
|
let public_address_books = self
|
|
|
|
|
.address_book_repository
|
|
|
|
|
.get_public_address_books()
|
|
|
|
|
.await?;
|
|
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
// Combine all address books, avoiding duplicates
|
|
|
|
|
let mut address_book_map = std::collections::HashMap::new();
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
for address_book in owned_address_books {
|
2026-02-08 13:40:23 +01:00
|
|
|
address_book_map.insert(*address_book.id(), address_book);
|
2025-04-13 01:04:04 +02:00
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
for address_book in shared_address_books {
|
2026-02-08 13:40:23 +01:00
|
|
|
address_book_map.insert(*address_book.id(), address_book);
|
2025-04-13 01:04:04 +02:00
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
for address_book in public_address_books {
|
2026-03-07 14:59:32 +01:00
|
|
|
if address_book.owner_id() != user_id.to_string()
|
2026-02-14 01:29:34 +01:00
|
|
|
&& !address_book_map.contains_key(address_book.id())
|
|
|
|
|
{
|
2026-02-08 13:40:23 +01:00
|
|
|
address_book_map.insert(*address_book.id(), address_book);
|
2025-04-13 01:04:04 +02:00
|
|
|
}
|
|
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
|
|
|
|
|
let address_books: Vec<AddressBookDto> = address_book_map
|
|
|
|
|
.values()
|
2025-04-13 01:04:04 +02:00
|
|
|
.cloned()
|
|
|
|
|
.map(AddressBookDto::from)
|
|
|
|
|
.collect();
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
Ok(address_books)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn list_public_address_books(&self) -> Result<Vec<AddressBookDto>, DomainError> {
|
2026-02-14 01:29:34 +01:00
|
|
|
let address_books = self
|
|
|
|
|
.address_book_repository
|
|
|
|
|
.get_public_address_books()
|
|
|
|
|
.await?;
|
|
|
|
|
let dtos: Vec<AddressBookDto> = address_books
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(AddressBookDto::from)
|
|
|
|
|
.collect();
|
2025-04-13 01:04:04 +02:00
|
|
|
Ok(dtos)
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
async fn share_address_book(
|
|
|
|
|
&self,
|
|
|
|
|
dto: ShareAddressBookDto,
|
2026-03-07 14:59:32 +01:00
|
|
|
user_id: Uuid,
|
2026-02-14 01:29:34 +01:00
|
|
|
) -> Result<(), DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let id = Uuid::parse_str(&dto.address_book_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid address book ID format"))?;
|
|
|
|
|
|
|
|
|
|
// Verify that the user is the owner of the address book
|
2026-02-14 01:29:34 +01:00
|
|
|
let address_book = self
|
|
|
|
|
.address_book_repository
|
|
|
|
|
.get_address_book_by_id(&id)
|
2025-04-13 01:04:04 +02:00
|
|
|
.await?
|
|
|
|
|
.ok_or_else(|| DomainError::not_found("Address book", "not found"))?;
|
|
|
|
|
|
2026-03-07 14:59:32 +01:00
|
|
|
if address_book.owner_id() != user_id.to_string() {
|
2026-02-14 01:29:34 +01:00
|
|
|
return Err(DomainError::unauthorized(
|
|
|
|
|
"Only the owner can share an address book",
|
|
|
|
|
));
|
2025-04-13 01:04:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Don't allow sharing with yourself
|
2026-03-07 14:59:32 +01:00
|
|
|
if dto.user_id == user_id.to_string() {
|
2026-02-14 01:29:34 +01:00
|
|
|
return Err(DomainError::validation_error(
|
|
|
|
|
"Cannot share an address book with yourself",
|
|
|
|
|
));
|
2025-04-13 01:04:04 +02:00
|
|
|
}
|
|
|
|
|
|
2026-03-07 14:59:32 +01:00
|
|
|
let target_user_id = Uuid::parse_str(&dto.user_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid target user ID format"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
self.address_book_repository
|
2026-03-07 14:59:32 +01:00
|
|
|
.share_address_book(&id, target_user_id, dto.can_write)
|
2026-02-14 01:29:34 +01:00
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
async fn unshare_address_book(
|
|
|
|
|
&self,
|
|
|
|
|
dto: UnshareAddressBookDto,
|
2026-03-07 14:59:32 +01:00
|
|
|
user_id: Uuid,
|
2026-02-14 01:29:34 +01:00
|
|
|
) -> Result<(), DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let id = Uuid::parse_str(&dto.address_book_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid address book ID format"))?;
|
|
|
|
|
|
|
|
|
|
// Verify that the user is the owner of the address book
|
2026-02-14 01:29:34 +01:00
|
|
|
let address_book = self
|
|
|
|
|
.address_book_repository
|
|
|
|
|
.get_address_book_by_id(&id)
|
2025-04-13 01:04:04 +02:00
|
|
|
.await?
|
|
|
|
|
.ok_or_else(|| DomainError::not_found("Address book", "not found"))?;
|
|
|
|
|
|
2026-03-07 14:59:32 +01:00
|
|
|
if address_book.owner_id() != user_id.to_string() {
|
2026-02-14 01:29:34 +01:00
|
|
|
return Err(DomainError::unauthorized(
|
|
|
|
|
"Only the owner can unshare an address book",
|
|
|
|
|
));
|
2025-04-13 01:04:04 +02:00
|
|
|
}
|
|
|
|
|
|
2026-03-07 14:59:32 +01:00
|
|
|
let target_user_id = Uuid::parse_str(&dto.user_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid target user ID format"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
self.address_book_repository
|
2026-03-07 14:59:32 +01:00
|
|
|
.unshare_address_book(&id, target_user_id)
|
2026-02-14 01:29:34 +01:00
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
async fn get_address_book_shares(
|
|
|
|
|
&self,
|
|
|
|
|
address_book_id: &str,
|
2026-03-07 14:59:32 +01:00
|
|
|
user_id: Uuid,
|
2026-02-14 01:29:34 +01:00
|
|
|
) -> Result<Vec<(String, bool)>, DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let id = Uuid::parse_str(address_book_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid address book ID format"))?;
|
|
|
|
|
|
|
|
|
|
// Verify that the user is the owner of the address book
|
2026-02-14 01:29:34 +01:00
|
|
|
let address_book = self
|
|
|
|
|
.address_book_repository
|
|
|
|
|
.get_address_book_by_id(&id)
|
2025-04-13 01:04:04 +02:00
|
|
|
.await?
|
|
|
|
|
.ok_or_else(|| DomainError::not_found("Address book", "not found"))?;
|
|
|
|
|
|
2026-03-07 14:59:32 +01:00
|
|
|
if address_book.owner_id() != user_id.to_string() {
|
2026-02-14 01:29:34 +01:00
|
|
|
return Err(DomainError::unauthorized(
|
|
|
|
|
"Only the owner can view address book shares",
|
|
|
|
|
));
|
2025-04-13 01:04:04 +02:00
|
|
|
}
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
let shares = self
|
|
|
|
|
.address_book_repository
|
|
|
|
|
.get_address_book_shares(&id)
|
|
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
Ok(shares)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ContactUseCase for ContactService {
|
|
|
|
|
async fn create_contact(&self, dto: CreateContactDto) -> Result<ContactDto, DomainError> {
|
|
|
|
|
let address_book_id = Uuid::parse_str(&dto.address_book_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid address book ID format"))?;
|
|
|
|
|
|
|
|
|
|
// Check if user has write access to the address book
|
2026-03-09 14:34:07 +01:00
|
|
|
self.check_address_book_write_access(
|
|
|
|
|
&address_book_id,
|
|
|
|
|
&Uuid::parse_str(&dto.user_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid user ID format"))?,
|
|
|
|
|
)
|
|
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
// Convert DTOs to domain entities
|
2026-02-14 01:29:34 +01:00
|
|
|
let email: Vec<Email> = dto
|
|
|
|
|
.email
|
|
|
|
|
.into_iter()
|
2025-04-13 01:04:04 +02:00
|
|
|
.map(|e| Email {
|
|
|
|
|
email: e.email,
|
|
|
|
|
r#type: e.r#type,
|
|
|
|
|
is_primary: e.is_primary,
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
let phone: Vec<Phone> = dto
|
|
|
|
|
.phone
|
|
|
|
|
.into_iter()
|
2025-04-13 01:04:04 +02:00
|
|
|
.map(|p| Phone {
|
|
|
|
|
number: p.number,
|
|
|
|
|
r#type: p.r#type,
|
|
|
|
|
is_primary: p.is_primary,
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
let address: Vec<Address> = dto
|
|
|
|
|
.address
|
|
|
|
|
.into_iter()
|
2025-04-13 01:04:04 +02:00
|
|
|
.map(|a| Address {
|
|
|
|
|
street: a.street,
|
|
|
|
|
city: a.city,
|
|
|
|
|
state: a.state,
|
|
|
|
|
postal_code: a.postal_code,
|
|
|
|
|
country: a.country,
|
|
|
|
|
r#type: a.r#type,
|
|
|
|
|
is_primary: a.is_primary,
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
|
2026-02-08 13:40:23 +01:00
|
|
|
let mut contact = Contact::new(
|
2025-04-13 01:04:04 +02:00
|
|
|
address_book_id,
|
2026-02-08 13:40:23 +01:00
|
|
|
dto.full_name,
|
|
|
|
|
dto.first_name,
|
|
|
|
|
dto.last_name,
|
|
|
|
|
dto.nickname,
|
2025-04-13 01:04:04 +02:00
|
|
|
email,
|
|
|
|
|
phone,
|
|
|
|
|
address,
|
2026-02-08 13:40:23 +01:00
|
|
|
dto.organization,
|
|
|
|
|
dto.title,
|
|
|
|
|
dto.notes,
|
|
|
|
|
dto.photo_url,
|
|
|
|
|
dto.birthday,
|
|
|
|
|
dto.anniversary,
|
|
|
|
|
String::new(), // Will be generated after creation
|
|
|
|
|
);
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
// Generate vCard data
|
|
|
|
|
let vcard = self.generate_vcard(&contact);
|
2026-02-08 13:40:23 +01:00
|
|
|
contact.set_vcard(vcard);
|
|
|
|
|
let contact_with_vcard = contact;
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
// Create the contact
|
2026-02-14 01:29:34 +01:00
|
|
|
let created_contact = self
|
|
|
|
|
.contact_repository
|
|
|
|
|
.create_contact(contact_with_vcard)
|
|
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
Ok(ContactDto::from(created_contact))
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
async fn create_contact_from_vcard(
|
|
|
|
|
&self,
|
|
|
|
|
dto: CreateContactVCardDto,
|
|
|
|
|
) -> Result<ContactDto, DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let address_book_id = Uuid::parse_str(&dto.address_book_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid address book ID format"))?;
|
|
|
|
|
|
|
|
|
|
// Check if user has write access to the address book
|
2026-03-09 14:34:07 +01:00
|
|
|
self.check_address_book_write_access(
|
|
|
|
|
&address_book_id,
|
|
|
|
|
&Uuid::parse_str(&dto.user_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid user ID format"))?,
|
|
|
|
|
)
|
|
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
// Parse vCard data
|
|
|
|
|
let mut contact = self.parse_vcard(&dto.vcard)?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
// Set address book ID
|
2026-02-08 13:40:23 +01:00
|
|
|
contact.set_address_book_id(address_book_id);
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2026-02-08 13:40:23 +01:00
|
|
|
// The contact was created with Contact::default() which generates a new ID
|
2025-04-13 01:04:04 +02:00
|
|
|
// Set creation and update timestamps
|
|
|
|
|
let now = Utc::now();
|
2026-02-08 13:40:23 +01:00
|
|
|
contact.set_updated_at(now);
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
// Create the contact
|
|
|
|
|
let created_contact = self.contact_repository.create_contact(contact).await?;
|
|
|
|
|
Ok(ContactDto::from(created_contact))
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
async fn update_contact(
|
|
|
|
|
&self,
|
|
|
|
|
contact_id: &str,
|
|
|
|
|
update: UpdateContactDto,
|
|
|
|
|
) -> Result<ContactDto, DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let id = Uuid::parse_str(contact_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid contact ID format"))?;
|
|
|
|
|
|
|
|
|
|
// Get the current contact
|
2026-02-14 01:29:34 +01:00
|
|
|
let contact = self
|
|
|
|
|
.contact_repository
|
|
|
|
|
.get_contact_by_id(&id)
|
2025-04-13 01:04:04 +02:00
|
|
|
.await?
|
|
|
|
|
.ok_or_else(|| DomainError::not_found("Contact", "not found"))?;
|
|
|
|
|
|
|
|
|
|
// Check if user has write access to the address book
|
2026-03-07 14:59:32 +01:00
|
|
|
let update_user_id = Uuid::parse_str(&update.user_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid user ID format"))?;
|
|
|
|
|
self.check_address_book_write_access(contact.address_book_id(), &update_user_id)
|
2026-02-14 01:29:34 +01:00
|
|
|
.await?;
|
2026-02-08 13:40:23 +01:00
|
|
|
|
|
|
|
|
// Destructure contact into owned parts for updates
|
|
|
|
|
let parts = contact.into_parts();
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
// Convert DTO fields to domain entities
|
|
|
|
|
let email = if let Some(email_dtos) = update.email {
|
2026-02-14 01:29:34 +01:00
|
|
|
email_dtos
|
|
|
|
|
.into_iter()
|
2025-04-13 01:04:04 +02:00
|
|
|
.map(|e| Email {
|
|
|
|
|
email: e.email,
|
|
|
|
|
r#type: e.r#type,
|
|
|
|
|
is_primary: e.is_primary,
|
|
|
|
|
})
|
|
|
|
|
.collect()
|
|
|
|
|
} else {
|
2026-02-08 13:40:23 +01:00
|
|
|
parts.email
|
2025-04-13 01:04:04 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let phone = if let Some(phone_dtos) = update.phone {
|
2026-02-14 01:29:34 +01:00
|
|
|
phone_dtos
|
|
|
|
|
.into_iter()
|
2025-04-13 01:04:04 +02:00
|
|
|
.map(|p| Phone {
|
|
|
|
|
number: p.number,
|
|
|
|
|
r#type: p.r#type,
|
|
|
|
|
is_primary: p.is_primary,
|
|
|
|
|
})
|
|
|
|
|
.collect()
|
|
|
|
|
} else {
|
2026-02-08 13:40:23 +01:00
|
|
|
parts.phone
|
2025-04-13 01:04:04 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let address = if let Some(address_dtos) = update.address {
|
2026-02-14 01:29:34 +01:00
|
|
|
address_dtos
|
|
|
|
|
.into_iter()
|
2025-04-13 01:04:04 +02:00
|
|
|
.map(|a| Address {
|
|
|
|
|
street: a.street,
|
|
|
|
|
city: a.city,
|
|
|
|
|
state: a.state,
|
|
|
|
|
postal_code: a.postal_code,
|
|
|
|
|
country: a.country,
|
|
|
|
|
r#type: a.r#type,
|
|
|
|
|
is_primary: a.is_primary,
|
|
|
|
|
})
|
|
|
|
|
.collect()
|
|
|
|
|
} else {
|
2026-02-08 13:40:23 +01:00
|
|
|
parts.address
|
2025-04-13 01:04:04 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Update the contact object
|
2026-02-08 13:40:23 +01:00
|
|
|
let mut updated_contact = Contact::from_raw(
|
2025-04-13 01:04:04 +02:00
|
|
|
id,
|
2026-02-08 13:40:23 +01:00
|
|
|
parts.address_book_id,
|
|
|
|
|
parts.uid,
|
|
|
|
|
update.full_name.or(parts.full_name),
|
|
|
|
|
update.first_name.or(parts.first_name),
|
|
|
|
|
update.last_name.or(parts.last_name),
|
|
|
|
|
update.nickname.or(parts.nickname),
|
2025-04-13 01:04:04 +02:00
|
|
|
email,
|
|
|
|
|
phone,
|
|
|
|
|
address,
|
2026-02-08 13:40:23 +01:00
|
|
|
update.organization.or(parts.organization),
|
|
|
|
|
update.title.or(parts.title),
|
|
|
|
|
update.notes.or(parts.notes),
|
|
|
|
|
update.photo_url.or(parts.photo_url),
|
|
|
|
|
update.birthday.or(parts.birthday),
|
|
|
|
|
update.anniversary.or(parts.anniversary),
|
2026-02-14 01:29:34 +01:00
|
|
|
parts.vcard, // Will be regenerated
|
2026-02-08 13:40:23 +01:00
|
|
|
Uuid::new_v4().to_string(), // Generate new ETag
|
|
|
|
|
parts.created_at,
|
|
|
|
|
Utc::now(),
|
|
|
|
|
);
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
// Generate new vCard data
|
|
|
|
|
let vcard = self.generate_vcard(&updated_contact);
|
2026-02-08 13:40:23 +01:00
|
|
|
updated_contact.set_vcard(vcard);
|
|
|
|
|
let contact_with_vcard = updated_contact;
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
// Update the contact
|
2026-02-14 01:29:34 +01:00
|
|
|
let result = self
|
|
|
|
|
.contact_repository
|
|
|
|
|
.update_contact(contact_with_vcard)
|
|
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
Ok(ContactDto::from(result))
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-07 14:59:32 +01:00
|
|
|
async fn delete_contact(&self, contact_id: &str, user_id: Uuid) -> Result<(), DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let id = Uuid::parse_str(contact_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid contact ID format"))?;
|
|
|
|
|
|
|
|
|
|
// Get the current contact
|
2026-02-14 01:29:34 +01:00
|
|
|
let contact = self
|
|
|
|
|
.contact_repository
|
|
|
|
|
.get_contact_by_id(&id)
|
2025-04-13 01:04:04 +02:00
|
|
|
.await?
|
|
|
|
|
.ok_or_else(|| DomainError::not_found("Contact", "not found"))?;
|
|
|
|
|
|
|
|
|
|
// Check if user has write access to the address book
|
2026-03-07 14:59:32 +01:00
|
|
|
self.check_address_book_write_access(contact.address_book_id(), &user_id)
|
2026-02-14 01:29:34 +01:00
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
// Delete the contact
|
|
|
|
|
self.contact_repository.delete_contact(&id).await?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
async fn get_contact(
|
|
|
|
|
&self,
|
|
|
|
|
contact_id: &str,
|
2026-03-07 14:59:32 +01:00
|
|
|
user_id: Uuid,
|
2026-02-14 01:29:34 +01:00
|
|
|
) -> Result<ContactDto, DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let id = Uuid::parse_str(contact_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid contact ID format"))?;
|
|
|
|
|
|
|
|
|
|
// Get the contact
|
2026-02-14 01:29:34 +01:00
|
|
|
let contact = self
|
|
|
|
|
.contact_repository
|
|
|
|
|
.get_contact_by_id(&id)
|
2025-04-13 01:04:04 +02:00
|
|
|
.await?
|
|
|
|
|
.ok_or_else(|| DomainError::not_found("Contact", "not found"))?;
|
|
|
|
|
|
|
|
|
|
// Check if user has access to the address book
|
2026-03-07 14:59:32 +01:00
|
|
|
self.check_address_book_access(contact.address_book_id(), &user_id)
|
2026-02-14 01:29:34 +01:00
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
Ok(ContactDto::from(contact))
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
async fn list_contacts(
|
|
|
|
|
&self,
|
|
|
|
|
address_book_id: &str,
|
2026-03-07 14:59:32 +01:00
|
|
|
user_id: Uuid,
|
2026-02-14 01:29:34 +01:00
|
|
|
) -> Result<Vec<ContactDto>, DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let id = Uuid::parse_str(address_book_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid address book ID format"))?;
|
|
|
|
|
|
|
|
|
|
// Check if user has access to the address book
|
2026-03-07 14:59:32 +01:00
|
|
|
self.check_address_book_access(&id, &user_id).await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
// Get contacts
|
2026-02-14 01:29:34 +01:00
|
|
|
let contacts = self
|
|
|
|
|
.contact_repository
|
|
|
|
|
.get_contacts_by_address_book(&id)
|
|
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
let dtos = contacts.into_iter().map(ContactDto::from).collect();
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
Ok(dtos)
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
async fn search_contacts(
|
|
|
|
|
&self,
|
|
|
|
|
address_book_id: &str,
|
|
|
|
|
query: &str,
|
2026-03-07 14:59:32 +01:00
|
|
|
user_id: Uuid,
|
2026-02-14 01:29:34 +01:00
|
|
|
) -> Result<Vec<ContactDto>, DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let id = Uuid::parse_str(address_book_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid address book ID format"))?;
|
|
|
|
|
|
|
|
|
|
// Check if user has access to the address book
|
2026-03-07 14:59:32 +01:00
|
|
|
self.check_address_book_access(&id, &user_id).await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
// Search contacts
|
|
|
|
|
let contacts = self.contact_repository.search_contacts(&id, query).await?;
|
|
|
|
|
let dtos = contacts.into_iter().map(ContactDto::from).collect();
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
Ok(dtos)
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
async fn create_group(
|
|
|
|
|
&self,
|
|
|
|
|
dto: CreateContactGroupDto,
|
|
|
|
|
) -> Result<ContactGroupDto, DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let address_book_id = Uuid::parse_str(&dto.address_book_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid address book ID format"))?;
|
|
|
|
|
|
|
|
|
|
// Check if user has write access to the address book
|
2026-03-09 14:34:07 +01:00
|
|
|
self.check_address_book_write_access(
|
|
|
|
|
&address_book_id,
|
|
|
|
|
&Uuid::parse_str(&dto.user_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid user ID format"))?,
|
|
|
|
|
)
|
|
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
let group = ContactGroup::new(address_book_id, dto.name);
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
let created_group = self.contact_group_repository.create_group(group).await?;
|
|
|
|
|
Ok(ContactGroupDto::from(created_group))
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
async fn update_group(
|
|
|
|
|
&self,
|
|
|
|
|
group_id: &str,
|
|
|
|
|
update: UpdateContactGroupDto,
|
|
|
|
|
) -> Result<ContactGroupDto, DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let id = Uuid::parse_str(group_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid group ID format"))?;
|
|
|
|
|
|
|
|
|
|
// Get the current group
|
2026-02-14 01:29:34 +01:00
|
|
|
let group = self
|
|
|
|
|
.contact_group_repository
|
|
|
|
|
.get_group_by_id(&id)
|
2025-04-13 01:04:04 +02:00
|
|
|
.await?
|
|
|
|
|
.ok_or_else(|| DomainError::not_found("Contact group", "not found"))?;
|
|
|
|
|
|
|
|
|
|
// Check if user has write access to the address book
|
2026-03-09 14:34:07 +01:00
|
|
|
self.check_address_book_write_access(
|
|
|
|
|
group.address_book_id(),
|
|
|
|
|
&Uuid::parse_str(&update.user_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid user ID format"))?,
|
|
|
|
|
)
|
|
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
// Update the group
|
2026-02-08 13:40:23 +01:00
|
|
|
let updated_group = ContactGroup::from_raw(
|
2025-04-13 01:04:04 +02:00
|
|
|
id,
|
2026-02-08 13:40:23 +01:00
|
|
|
*group.address_book_id(),
|
|
|
|
|
update.name,
|
|
|
|
|
*group.created_at(),
|
|
|
|
|
Utc::now(),
|
|
|
|
|
);
|
2025-04-13 01:04:04 +02:00
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
let result = self
|
|
|
|
|
.contact_group_repository
|
|
|
|
|
.update_group(updated_group)
|
|
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
Ok(ContactGroupDto::from(result))
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-07 14:59:32 +01:00
|
|
|
async fn delete_group(&self, group_id: &str, user_id: Uuid) -> Result<(), DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let id = Uuid::parse_str(group_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid group ID format"))?;
|
|
|
|
|
|
|
|
|
|
// Get the current group
|
2026-02-14 01:29:34 +01:00
|
|
|
let group = self
|
|
|
|
|
.contact_group_repository
|
|
|
|
|
.get_group_by_id(&id)
|
2025-04-13 01:04:04 +02:00
|
|
|
.await?
|
|
|
|
|
.ok_or_else(|| DomainError::not_found("Contact group", "not found"))?;
|
|
|
|
|
|
|
|
|
|
// Check if user has write access to the address book
|
2026-03-07 14:59:32 +01:00
|
|
|
self.check_address_book_write_access(group.address_book_id(), &user_id)
|
2026-02-14 01:29:34 +01:00
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
// Delete the group
|
|
|
|
|
self.contact_group_repository.delete_group(&id).await?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
async fn get_group(
|
|
|
|
|
&self,
|
|
|
|
|
group_id: &str,
|
2026-03-07 14:59:32 +01:00
|
|
|
user_id: Uuid,
|
2026-02-14 01:29:34 +01:00
|
|
|
) -> Result<ContactGroupDto, DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let id = Uuid::parse_str(group_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid group ID format"))?;
|
|
|
|
|
|
|
|
|
|
// Get the group
|
2026-02-14 01:29:34 +01:00
|
|
|
let group = self
|
|
|
|
|
.contact_group_repository
|
|
|
|
|
.get_group_by_id(&id)
|
2025-04-13 01:04:04 +02:00
|
|
|
.await?
|
|
|
|
|
.ok_or_else(|| DomainError::not_found("Contact group", "not found"))?;
|
|
|
|
|
|
|
|
|
|
// Check if user has access to the address book
|
2026-03-07 14:59:32 +01:00
|
|
|
self.check_address_book_access(group.address_book_id(), &user_id)
|
2026-02-14 01:29:34 +01:00
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
// Get the number of contacts in the group
|
2026-02-14 01:29:34 +01:00
|
|
|
let contacts = self
|
|
|
|
|
.contact_group_repository
|
|
|
|
|
.get_contacts_in_group(&id)
|
|
|
|
|
.await?;
|
|
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
let mut dto = ContactGroupDto::from(group);
|
|
|
|
|
dto.members_count = Some(contacts.len() as i32);
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
Ok(dto)
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
async fn list_groups(
|
|
|
|
|
&self,
|
|
|
|
|
address_book_id: &str,
|
2026-03-07 14:59:32 +01:00
|
|
|
user_id: Uuid,
|
2026-02-14 01:29:34 +01:00
|
|
|
) -> Result<Vec<ContactGroupDto>, DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let id = Uuid::parse_str(address_book_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid address book ID format"))?;
|
|
|
|
|
|
|
|
|
|
// Check if user has access to the address book
|
2026-03-07 14:59:32 +01:00
|
|
|
self.check_address_book_access(&id, &user_id).await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
// Get groups
|
2026-02-14 01:29:34 +01:00
|
|
|
let groups = self
|
|
|
|
|
.contact_group_repository
|
|
|
|
|
.get_groups_by_address_book(&id)
|
|
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
let dtos = groups.into_iter().map(ContactGroupDto::from).collect();
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
Ok(dtos)
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
async fn add_contact_to_group(
|
|
|
|
|
&self,
|
|
|
|
|
dto: GroupMembershipDto,
|
2026-03-07 14:59:32 +01:00
|
|
|
user_id: Uuid,
|
2026-02-14 01:29:34 +01:00
|
|
|
) -> Result<(), DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let group_id = Uuid::parse_str(&dto.group_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid group ID format"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
let contact_id = Uuid::parse_str(&dto.contact_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid contact ID format"))?;
|
|
|
|
|
|
|
|
|
|
// Get the group
|
2026-02-14 01:29:34 +01:00
|
|
|
let group = self
|
|
|
|
|
.contact_group_repository
|
|
|
|
|
.get_group_by_id(&group_id)
|
2025-04-13 01:04:04 +02:00
|
|
|
.await?
|
|
|
|
|
.ok_or_else(|| DomainError::not_found("Contact group", "not found"))?;
|
|
|
|
|
|
|
|
|
|
// Check if user has write access to the address book
|
2026-03-07 14:59:32 +01:00
|
|
|
self.check_address_book_write_access(group.address_book_id(), &user_id)
|
2026-02-14 01:29:34 +01:00
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
// Add contact to group
|
2026-02-14 01:29:34 +01:00
|
|
|
self.contact_group_repository
|
|
|
|
|
.add_contact_to_group(&group_id, &contact_id)
|
|
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
async fn remove_contact_from_group(
|
|
|
|
|
&self,
|
|
|
|
|
dto: GroupMembershipDto,
|
2026-03-07 14:59:32 +01:00
|
|
|
user_id: Uuid,
|
2026-02-14 01:29:34 +01:00
|
|
|
) -> Result<(), DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let group_id = Uuid::parse_str(&dto.group_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid group ID format"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
let contact_id = Uuid::parse_str(&dto.contact_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid contact ID format"))?;
|
|
|
|
|
|
|
|
|
|
// Get the group
|
2026-02-14 01:29:34 +01:00
|
|
|
let group = self
|
|
|
|
|
.contact_group_repository
|
|
|
|
|
.get_group_by_id(&group_id)
|
2025-04-13 01:04:04 +02:00
|
|
|
.await?
|
|
|
|
|
.ok_or_else(|| DomainError::not_found("Contact group", "not found"))?;
|
|
|
|
|
|
|
|
|
|
// Check if user has write access to the address book
|
2026-03-07 14:59:32 +01:00
|
|
|
self.check_address_book_write_access(group.address_book_id(), &user_id)
|
2026-02-14 01:29:34 +01:00
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
// Remove contact from group
|
2026-02-14 01:29:34 +01:00
|
|
|
self.contact_group_repository
|
|
|
|
|
.remove_contact_from_group(&group_id, &contact_id)
|
|
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
async fn list_contacts_in_group(
|
|
|
|
|
&self,
|
|
|
|
|
group_id: &str,
|
2026-03-07 14:59:32 +01:00
|
|
|
user_id: Uuid,
|
2026-02-14 01:29:34 +01:00
|
|
|
) -> Result<Vec<ContactDto>, DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let id = Uuid::parse_str(group_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid group ID format"))?;
|
|
|
|
|
|
|
|
|
|
// Get the group
|
2026-02-14 01:29:34 +01:00
|
|
|
let group = self
|
|
|
|
|
.contact_group_repository
|
|
|
|
|
.get_group_by_id(&id)
|
2025-04-13 01:04:04 +02:00
|
|
|
.await?
|
|
|
|
|
.ok_or_else(|| DomainError::not_found("Contact group", "not found"))?;
|
|
|
|
|
|
|
|
|
|
// Check if user has access to the address book
|
2026-03-07 14:59:32 +01:00
|
|
|
self.check_address_book_access(group.address_book_id(), &user_id)
|
2026-02-14 01:29:34 +01:00
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
// Get contacts in group
|
2026-02-14 01:29:34 +01:00
|
|
|
let contacts = self
|
|
|
|
|
.contact_group_repository
|
|
|
|
|
.get_contacts_in_group(&id)
|
|
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
let dtos = contacts.into_iter().map(ContactDto::from).collect();
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
Ok(dtos)
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
async fn list_groups_for_contact(
|
|
|
|
|
&self,
|
|
|
|
|
contact_id: &str,
|
2026-03-07 14:59:32 +01:00
|
|
|
user_id: Uuid,
|
2026-02-14 01:29:34 +01:00
|
|
|
) -> Result<Vec<ContactGroupDto>, DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let id = Uuid::parse_str(contact_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid contact ID format"))?;
|
|
|
|
|
|
|
|
|
|
// Get the contact
|
2026-02-14 01:29:34 +01:00
|
|
|
let contact = self
|
|
|
|
|
.contact_repository
|
|
|
|
|
.get_contact_by_id(&id)
|
2025-04-13 01:04:04 +02:00
|
|
|
.await?
|
|
|
|
|
.ok_or_else(|| DomainError::not_found("Contact", "not found"))?;
|
|
|
|
|
|
|
|
|
|
// Check if user has access to the address book
|
2026-03-07 14:59:32 +01:00
|
|
|
self.check_address_book_access(contact.address_book_id(), &user_id)
|
2026-02-14 01:29:34 +01:00
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
// Get groups for contact
|
2026-02-14 01:29:34 +01:00
|
|
|
let groups = self
|
|
|
|
|
.contact_group_repository
|
|
|
|
|
.get_groups_for_contact(&id)
|
|
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
let dtos = groups.into_iter().map(ContactGroupDto::from).collect();
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
Ok(dtos)
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
async fn get_contact_vcard(
|
|
|
|
|
&self,
|
|
|
|
|
contact_id: &str,
|
2026-03-07 14:59:32 +01:00
|
|
|
user_id: Uuid,
|
2026-02-14 01:29:34 +01:00
|
|
|
) -> Result<String, DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
let id = Uuid::parse_str(contact_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid contact ID format"))?;
|
|
|
|
|
|
|
|
|
|
// Get the contact
|
2026-02-14 01:29:34 +01:00
|
|
|
let contact = self
|
|
|
|
|
.contact_repository
|
|
|
|
|
.get_contact_by_id(&id)
|
2025-04-13 01:04:04 +02:00
|
|
|
.await?
|
|
|
|
|
.ok_or_else(|| DomainError::not_found("Contact", "not found"))?;
|
|
|
|
|
|
|
|
|
|
// Check if user has access to the address book
|
2026-03-07 14:59:32 +01:00
|
|
|
self.check_address_book_access(contact.address_book_id(), &user_id)
|
2026-02-14 01:29:34 +01:00
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
// Return the vCard data
|
2026-02-08 13:40:23 +01:00
|
|
|
Ok(contact.vcard().to_string())
|
2025-04-13 01:04:04 +02:00
|
|
|
}
|
|
|
|
|
|
2026-02-14 01:29:34 +01:00
|
|
|
async fn get_contacts_as_vcards(
|
|
|
|
|
&self,
|
|
|
|
|
address_book_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 id = Uuid::parse_str(address_book_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid address book ID format"))?;
|
|
|
|
|
|
|
|
|
|
// Check if user has access to the address book
|
2026-03-07 14:59:32 +01:00
|
|
|
self.check_address_book_access(&id, &user_id).await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
// Get all contacts in the address book
|
2026-02-14 01:29:34 +01:00
|
|
|
let contacts = self
|
|
|
|
|
.contact_repository
|
|
|
|
|
.get_contacts_by_address_book(&id)
|
|
|
|
|
.await?;
|
|
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
// Convert to Vec<(id, vcard)>
|
2026-02-14 01:29:34 +01:00
|
|
|
let vcards = contacts
|
|
|
|
|
.into_iter()
|
2026-02-08 13:40:23 +01:00
|
|
|
.map(|contact| (contact.id().to_string(), contact.vcard().to_string()))
|
2025-04-13 01:04:04 +02:00
|
|
|
.collect();
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
Ok(vcards)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl StorageUseCase for ContactService {
|
2026-02-14 01:29:34 +01:00
|
|
|
async fn handle_request(
|
|
|
|
|
&self,
|
|
|
|
|
action: &str,
|
|
|
|
|
params: serde_json::Value,
|
|
|
|
|
) -> Result<serde_json::Value, DomainError> {
|
2025-04-13 01:04:04 +02:00
|
|
|
match action {
|
|
|
|
|
// Address Book operations
|
|
|
|
|
"create_address_book" => {
|
2026-02-14 01:29:34 +01:00
|
|
|
let dto: CreateAddressBookDto =
|
|
|
|
|
serde_json::from_value(params.clone()).map_err(|e| {
|
|
|
|
|
DomainError::validation_error(format!("Invalid parameters: {}", e))
|
|
|
|
|
})?;
|
|
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
let result = self.create_address_book(dto).await?;
|
|
|
|
|
Ok(serde_json::to_value(result).unwrap())
|
2026-02-14 01:29:34 +01:00
|
|
|
}
|
2025-04-13 01:04:04 +02:00
|
|
|
"update_address_book" => {
|
2026-02-14 01:29:34 +01:00
|
|
|
let address_book_id = params["address_book_id"].as_str().ok_or_else(|| {
|
|
|
|
|
DomainError::validation_error("Missing address_book_id parameter")
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
let update: UpdateAddressBookDto =
|
|
|
|
|
serde_json::from_value(params.clone()).map_err(|e| {
|
|
|
|
|
DomainError::validation_error(format!("Invalid parameters: {}", e))
|
|
|
|
|
})?;
|
|
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
let result = self.update_address_book(address_book_id, update).await?;
|
|
|
|
|
Ok(serde_json::to_value(result).unwrap())
|
2026-02-14 01:29:34 +01:00
|
|
|
}
|
2025-04-13 01:04:04 +02:00
|
|
|
"delete_address_book" => {
|
2026-02-14 01:29:34 +01:00
|
|
|
let address_book_id = params["address_book_id"].as_str().ok_or_else(|| {
|
|
|
|
|
DomainError::validation_error("Missing address_book_id parameter")
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
let user_id = params["user_id"]
|
|
|
|
|
.as_str()
|
2025-04-13 01:04:04 +02:00
|
|
|
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
2026-03-07 14:59:32 +01:00
|
|
|
let user_id = Uuid::parse_str(user_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
self.delete_address_book(address_book_id, user_id).await?;
|
|
|
|
|
Ok(serde_json::Value::Null)
|
2026-02-14 01:29:34 +01:00
|
|
|
}
|
2025-04-13 01:04:04 +02:00
|
|
|
"get_address_book" => {
|
2026-02-14 01:29:34 +01:00
|
|
|
let address_book_id = params["address_book_id"].as_str().ok_or_else(|| {
|
|
|
|
|
DomainError::validation_error("Missing address_book_id parameter")
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
let user_id = params["user_id"]
|
|
|
|
|
.as_str()
|
2025-04-13 01:04:04 +02:00
|
|
|
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
2026-03-07 14:59:32 +01:00
|
|
|
let user_id = Uuid::parse_str(user_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
let result = self.get_address_book(address_book_id, user_id).await?;
|
|
|
|
|
Ok(serde_json::to_value(result).unwrap())
|
2026-02-14 01:29:34 +01:00
|
|
|
}
|
2025-04-13 01:04:04 +02:00
|
|
|
"list_user_address_books" => {
|
2026-02-14 01:29:34 +01:00
|
|
|
let user_id = params["user_id"]
|
|
|
|
|
.as_str()
|
2025-04-13 01:04:04 +02:00
|
|
|
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
2026-03-07 14:59:32 +01:00
|
|
|
let user_id = Uuid::parse_str(user_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
let result = self.list_user_address_books(user_id).await?;
|
|
|
|
|
Ok(serde_json::to_value(result).unwrap())
|
2026-02-14 01:29:34 +01:00
|
|
|
}
|
2025-04-13 01:04:04 +02:00
|
|
|
"list_public_address_books" => {
|
|
|
|
|
let result = self.list_public_address_books().await?;
|
|
|
|
|
Ok(serde_json::to_value(result).unwrap())
|
2026-02-14 01:29:34 +01:00
|
|
|
}
|
2025-04-13 01:04:04 +02:00
|
|
|
"share_address_book" => {
|
2026-02-14 01:29:34 +01:00
|
|
|
let dto: ShareAddressBookDto =
|
|
|
|
|
serde_json::from_value(params.clone()).map_err(|e| {
|
|
|
|
|
DomainError::validation_error(format!("Invalid parameters: {}", e))
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
let user_id = params["user_id"]
|
|
|
|
|
.as_str()
|
2025-04-13 01:04:04 +02:00
|
|
|
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
2026-03-07 14:59:32 +01:00
|
|
|
let user_id = Uuid::parse_str(user_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
self.share_address_book(dto, user_id).await?;
|
|
|
|
|
Ok(serde_json::Value::Null)
|
2026-02-14 01:29:34 +01:00
|
|
|
}
|
2025-04-13 01:04:04 +02:00
|
|
|
"unshare_address_book" => {
|
2026-02-14 01:29:34 +01:00
|
|
|
let dto: UnshareAddressBookDto =
|
|
|
|
|
serde_json::from_value(params.clone()).map_err(|e| {
|
|
|
|
|
DomainError::validation_error(format!("Invalid parameters: {}", e))
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
let user_id = params["user_id"]
|
|
|
|
|
.as_str()
|
2025-04-13 01:04:04 +02:00
|
|
|
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
2026-03-07 14:59:32 +01:00
|
|
|
let user_id = Uuid::parse_str(user_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
self.unshare_address_book(dto, user_id).await?;
|
|
|
|
|
Ok(serde_json::Value::Null)
|
2026-02-14 01:29:34 +01:00
|
|
|
}
|
2025-04-13 01:04:04 +02:00
|
|
|
"get_address_book_shares" => {
|
2026-02-14 01:29:34 +01:00
|
|
|
let address_book_id = params["address_book_id"].as_str().ok_or_else(|| {
|
|
|
|
|
DomainError::validation_error("Missing address_book_id parameter")
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
let user_id = params["user_id"]
|
|
|
|
|
.as_str()
|
2025-04-13 01:04:04 +02:00
|
|
|
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
2026-03-07 14:59:32 +01:00
|
|
|
let user_id = Uuid::parse_str(user_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
|
|
|
|
let result = self
|
|
|
|
|
.get_address_book_shares(address_book_id, user_id)
|
|
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
Ok(serde_json::to_value(result).unwrap())
|
2026-02-14 01:29:34 +01:00
|
|
|
}
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
// Contact operations
|
|
|
|
|
"create_contact" => {
|
2026-02-14 01:29:34 +01:00
|
|
|
let dto: CreateContactDto =
|
|
|
|
|
serde_json::from_value(params.clone()).map_err(|e| {
|
|
|
|
|
DomainError::validation_error(format!("Invalid parameters: {}", e))
|
|
|
|
|
})?;
|
|
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
let result = self.create_contact(dto).await?;
|
|
|
|
|
Ok(serde_json::to_value(result).unwrap())
|
2026-02-14 01:29:34 +01:00
|
|
|
}
|
2025-04-13 01:04:04 +02:00
|
|
|
"create_contact_from_vcard" => {
|
2026-02-14 01:29:34 +01:00
|
|
|
let dto: CreateContactVCardDto =
|
|
|
|
|
serde_json::from_value(params.clone()).map_err(|e| {
|
|
|
|
|
DomainError::validation_error(format!("Invalid parameters: {}", e))
|
|
|
|
|
})?;
|
|
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
let result = self.create_contact_from_vcard(dto).await?;
|
|
|
|
|
Ok(serde_json::to_value(result).unwrap())
|
2026-02-14 01:29:34 +01:00
|
|
|
}
|
2025-04-13 01:04:04 +02:00
|
|
|
"update_contact" => {
|
2026-02-14 01:29:34 +01:00
|
|
|
let contact_id = params["contact_id"]
|
|
|
|
|
.as_str()
|
2025-04-13 01:04:04 +02:00
|
|
|
.ok_or_else(|| DomainError::validation_error("Missing contact_id parameter"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
|
|
|
|
let update: UpdateContactDto =
|
|
|
|
|
serde_json::from_value(params.clone()).map_err(|e| {
|
|
|
|
|
DomainError::validation_error(format!("Invalid parameters: {}", e))
|
|
|
|
|
})?;
|
|
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
let result = self.update_contact(contact_id, update).await?;
|
|
|
|
|
Ok(serde_json::to_value(result).unwrap())
|
2026-02-14 01:29:34 +01:00
|
|
|
}
|
2025-04-13 01:04:04 +02:00
|
|
|
"delete_contact" => {
|
2026-02-14 01:29:34 +01:00
|
|
|
let contact_id = params["contact_id"]
|
|
|
|
|
.as_str()
|
2025-04-13 01:04:04 +02:00
|
|
|
.ok_or_else(|| DomainError::validation_error("Missing contact_id parameter"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
|
|
|
|
let user_id = params["user_id"]
|
|
|
|
|
.as_str()
|
2025-04-13 01:04:04 +02:00
|
|
|
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
2026-03-07 14:59:32 +01:00
|
|
|
let user_id = Uuid::parse_str(user_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
self.delete_contact(contact_id, user_id).await?;
|
|
|
|
|
Ok(serde_json::Value::Null)
|
2026-02-14 01:29:34 +01:00
|
|
|
}
|
2025-04-13 01:04:04 +02:00
|
|
|
"get_contact" => {
|
2026-02-14 01:29:34 +01:00
|
|
|
let contact_id = params["contact_id"]
|
|
|
|
|
.as_str()
|
2025-04-13 01:04:04 +02:00
|
|
|
.ok_or_else(|| DomainError::validation_error("Missing contact_id parameter"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
|
|
|
|
let user_id = params["user_id"]
|
|
|
|
|
.as_str()
|
2025-04-13 01:04:04 +02:00
|
|
|
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
2026-03-07 14:59:32 +01:00
|
|
|
let user_id = Uuid::parse_str(user_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
let result = self.get_contact(contact_id, user_id).await?;
|
|
|
|
|
Ok(serde_json::to_value(result).unwrap())
|
2026-02-14 01:29:34 +01:00
|
|
|
}
|
2025-04-13 01:04:04 +02:00
|
|
|
"list_contacts" => {
|
2026-02-14 01:29:34 +01:00
|
|
|
let address_book_id = params["address_book_id"].as_str().ok_or_else(|| {
|
|
|
|
|
DomainError::validation_error("Missing address_book_id parameter")
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
let user_id = params["user_id"]
|
|
|
|
|
.as_str()
|
2025-04-13 01:04:04 +02:00
|
|
|
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
2026-03-07 14:59:32 +01:00
|
|
|
let user_id = Uuid::parse_str(user_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
let result = self.list_contacts(address_book_id, user_id).await?;
|
|
|
|
|
Ok(serde_json::to_value(result).unwrap())
|
2026-02-14 01:29:34 +01:00
|
|
|
}
|
2025-04-13 01:04:04 +02:00
|
|
|
"search_contacts" => {
|
2026-02-14 01:29:34 +01:00
|
|
|
let address_book_id = params["address_book_id"].as_str().ok_or_else(|| {
|
|
|
|
|
DomainError::validation_error("Missing address_book_id parameter")
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
let query = params["query"]
|
|
|
|
|
.as_str()
|
2025-04-13 01:04:04 +02:00
|
|
|
.ok_or_else(|| DomainError::validation_error("Missing query parameter"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
|
|
|
|
let user_id = params["user_id"]
|
|
|
|
|
.as_str()
|
2025-04-13 01:04:04 +02:00
|
|
|
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
2026-03-07 14:59:32 +01:00
|
|
|
let user_id = Uuid::parse_str(user_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
|
|
|
|
let result = self
|
|
|
|
|
.search_contacts(address_book_id, query, user_id)
|
|
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
Ok(serde_json::to_value(result).unwrap())
|
2026-02-14 01:29:34 +01:00
|
|
|
}
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
// Group operations
|
|
|
|
|
"create_group" => {
|
2026-02-14 01:29:34 +01:00
|
|
|
let dto: CreateContactGroupDto =
|
|
|
|
|
serde_json::from_value(params.clone()).map_err(|e| {
|
|
|
|
|
DomainError::validation_error(format!("Invalid parameters: {}", e))
|
|
|
|
|
})?;
|
|
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
let result = self.create_group(dto).await?;
|
|
|
|
|
Ok(serde_json::to_value(result).unwrap())
|
2026-02-14 01:29:34 +01:00
|
|
|
}
|
2025-04-13 01:04:04 +02:00
|
|
|
"update_group" => {
|
2026-02-14 01:29:34 +01:00
|
|
|
let group_id = params["group_id"]
|
|
|
|
|
.as_str()
|
2025-04-13 01:04:04 +02:00
|
|
|
.ok_or_else(|| DomainError::validation_error("Missing group_id parameter"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
let update: UpdateContactGroupDto = serde_json::from_value(params.clone())
|
2026-02-14 01:29:34 +01:00
|
|
|
.map_err(|e| {
|
|
|
|
|
DomainError::validation_error(format!("Invalid parameters: {}", e))
|
|
|
|
|
})?;
|
|
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
let result = self.update_group(group_id, update).await?;
|
|
|
|
|
Ok(serde_json::to_value(result).unwrap())
|
2026-02-14 01:29:34 +01:00
|
|
|
}
|
2025-04-13 01:04:04 +02:00
|
|
|
"delete_group" => {
|
2026-02-14 01:29:34 +01:00
|
|
|
let group_id = params["group_id"]
|
|
|
|
|
.as_str()
|
2025-04-13 01:04:04 +02:00
|
|
|
.ok_or_else(|| DomainError::validation_error("Missing group_id parameter"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
|
|
|
|
let user_id = params["user_id"]
|
|
|
|
|
.as_str()
|
2025-04-13 01:04:04 +02:00
|
|
|
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
2026-03-07 14:59:32 +01:00
|
|
|
let user_id = Uuid::parse_str(user_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
self.delete_group(group_id, user_id).await?;
|
|
|
|
|
Ok(serde_json::Value::Null)
|
2026-02-14 01:29:34 +01:00
|
|
|
}
|
2025-04-13 01:04:04 +02:00
|
|
|
"get_group" => {
|
2026-02-14 01:29:34 +01:00
|
|
|
let group_id = params["group_id"]
|
|
|
|
|
.as_str()
|
2025-04-13 01:04:04 +02:00
|
|
|
.ok_or_else(|| DomainError::validation_error("Missing group_id parameter"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
|
|
|
|
let user_id = params["user_id"]
|
|
|
|
|
.as_str()
|
2025-04-13 01:04:04 +02:00
|
|
|
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
2026-03-07 14:59:32 +01:00
|
|
|
let user_id = Uuid::parse_str(user_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
let result = self.get_group(group_id, user_id).await?;
|
|
|
|
|
Ok(serde_json::to_value(result).unwrap())
|
2026-02-14 01:29:34 +01:00
|
|
|
}
|
2025-04-13 01:04:04 +02:00
|
|
|
"list_groups" => {
|
2026-02-14 01:29:34 +01:00
|
|
|
let address_book_id = params["address_book_id"].as_str().ok_or_else(|| {
|
|
|
|
|
DomainError::validation_error("Missing address_book_id parameter")
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
let user_id = params["user_id"]
|
|
|
|
|
.as_str()
|
2025-04-13 01:04:04 +02:00
|
|
|
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
2026-03-07 14:59:32 +01:00
|
|
|
let user_id = Uuid::parse_str(user_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
let result = self.list_groups(address_book_id, user_id).await?;
|
|
|
|
|
Ok(serde_json::to_value(result).unwrap())
|
2026-02-14 01:29:34 +01:00
|
|
|
}
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
// Group membership operations
|
|
|
|
|
"add_contact_to_group" => {
|
2026-02-14 01:29:34 +01:00
|
|
|
let dto: GroupMembershipDto =
|
|
|
|
|
serde_json::from_value(params.clone()).map_err(|e| {
|
|
|
|
|
DomainError::validation_error(format!("Invalid parameters: {}", e))
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
let user_id = params["user_id"]
|
|
|
|
|
.as_str()
|
2025-04-13 01:04:04 +02:00
|
|
|
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
2026-03-07 14:59:32 +01:00
|
|
|
let user_id = Uuid::parse_str(user_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
self.add_contact_to_group(dto, user_id).await?;
|
|
|
|
|
Ok(serde_json::Value::Null)
|
2026-02-14 01:29:34 +01:00
|
|
|
}
|
2025-04-13 01:04:04 +02:00
|
|
|
"remove_contact_from_group" => {
|
2026-02-14 01:29:34 +01:00
|
|
|
let dto: GroupMembershipDto =
|
|
|
|
|
serde_json::from_value(params.clone()).map_err(|e| {
|
|
|
|
|
DomainError::validation_error(format!("Invalid parameters: {}", e))
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
let user_id = params["user_id"]
|
|
|
|
|
.as_str()
|
2025-04-13 01:04:04 +02:00
|
|
|
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
2026-03-07 14:59:32 +01:00
|
|
|
let user_id = Uuid::parse_str(user_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
self.remove_contact_from_group(dto, user_id).await?;
|
|
|
|
|
Ok(serde_json::Value::Null)
|
2026-02-14 01:29:34 +01:00
|
|
|
}
|
2025-04-13 01:04:04 +02:00
|
|
|
"list_contacts_in_group" => {
|
2026-02-14 01:29:34 +01:00
|
|
|
let group_id = params["group_id"]
|
|
|
|
|
.as_str()
|
2025-04-13 01:04:04 +02:00
|
|
|
.ok_or_else(|| DomainError::validation_error("Missing group_id parameter"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
|
|
|
|
let user_id = params["user_id"]
|
|
|
|
|
.as_str()
|
2025-04-13 01:04:04 +02:00
|
|
|
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
2026-03-07 14:59:32 +01:00
|
|
|
let user_id = Uuid::parse_str(user_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
let result = self.list_contacts_in_group(group_id, user_id).await?;
|
|
|
|
|
Ok(serde_json::to_value(result).unwrap())
|
2026-02-14 01:29:34 +01:00
|
|
|
}
|
2025-04-13 01:04:04 +02:00
|
|
|
"list_groups_for_contact" => {
|
2026-02-14 01:29:34 +01:00
|
|
|
let contact_id = params["contact_id"]
|
|
|
|
|
.as_str()
|
2025-04-13 01:04:04 +02:00
|
|
|
.ok_or_else(|| DomainError::validation_error("Missing contact_id parameter"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
|
|
|
|
let user_id = params["user_id"]
|
|
|
|
|
.as_str()
|
2025-04-13 01:04:04 +02:00
|
|
|
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
2026-03-07 14:59:32 +01:00
|
|
|
let user_id = Uuid::parse_str(user_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
let result = self.list_groups_for_contact(contact_id, user_id).await?;
|
|
|
|
|
Ok(serde_json::to_value(result).unwrap())
|
2026-02-14 01:29:34 +01:00
|
|
|
}
|
2025-04-13 01:04:04 +02:00
|
|
|
|
|
|
|
|
// vCard operations
|
|
|
|
|
"get_contact_vcard" => {
|
2026-02-14 01:29:34 +01:00
|
|
|
let contact_id = params["contact_id"]
|
|
|
|
|
.as_str()
|
2025-04-13 01:04:04 +02:00
|
|
|
.ok_or_else(|| DomainError::validation_error("Missing contact_id parameter"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
|
|
|
|
let user_id = params["user_id"]
|
|
|
|
|
.as_str()
|
2025-04-13 01:04:04 +02:00
|
|
|
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
2026-03-07 14:59:32 +01:00
|
|
|
let user_id = Uuid::parse_str(user_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
2025-04-13 01:04:04 +02:00
|
|
|
let result = self.get_contact_vcard(contact_id, user_id).await?;
|
|
|
|
|
Ok(serde_json::to_value(result).unwrap())
|
2026-02-14 01:29:34 +01:00
|
|
|
}
|
2025-04-13 01:04:04 +02:00
|
|
|
"get_contacts_as_vcards" => {
|
2026-02-14 01:29:34 +01:00
|
|
|
let address_book_id = params["address_book_id"].as_str().ok_or_else(|| {
|
|
|
|
|
DomainError::validation_error("Missing address_book_id parameter")
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
let user_id = params["user_id"]
|
|
|
|
|
.as_str()
|
2025-04-13 01:04:04 +02:00
|
|
|
.ok_or_else(|| DomainError::validation_error("Missing user_id parameter"))?;
|
2026-03-07 14:59:32 +01:00
|
|
|
let user_id = Uuid::parse_str(user_id)
|
|
|
|
|
.map_err(|_| DomainError::validation_error("Invalid user_id format"))?;
|
2026-02-14 01:29:34 +01:00
|
|
|
|
|
|
|
|
let result = self
|
|
|
|
|
.get_contacts_as_vcards(address_book_id, user_id)
|
|
|
|
|
.await?;
|
2025-04-13 01:04:04 +02:00
|
|
|
Ok(serde_json::to_value(result).unwrap())
|
2026-02-14 01:29:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_ => Err(DomainError::validation_error(format!(
|
|
|
|
|
"Unknown action: {}",
|
|
|
|
|
action
|
|
|
|
|
))),
|
2025-04-13 01:04:04 +02:00
|
|
|
}
|
|
|
|
|
}
|
2026-02-14 01:29:34 +01:00
|
|
|
}
|