koprogo_api/application/use_cases/
building_use_cases.rs1use crate::application::dto::{
2 BuildingFilters, BuildingResponseDto, CreateBuildingDto, PageRequest, UpdateBuildingDto,
3};
4use crate::application::ports::BuildingRepository;
5use crate::domain::entities::Building;
6use std::sync::Arc;
7use uuid::Uuid;
8
9pub struct BuildingUseCases {
10 repository: Arc<dyn BuildingRepository>,
11}
12
13impl BuildingUseCases {
14 pub fn new(repository: Arc<dyn BuildingRepository>) -> Self {
15 Self { repository }
16 }
17
18 pub async fn create_building(
19 &self,
20 dto: CreateBuildingDto,
21 ) -> Result<BuildingResponseDto, String> {
22 let organization_id = Uuid::parse_str(&dto.organization_id)
23 .map_err(|_| "Invalid organization_id format".to_string())?;
24
25 let building = Building::new(
26 organization_id,
27 dto.name,
28 dto.address,
29 dto.city,
30 dto.postal_code,
31 dto.country,
32 dto.total_units,
33 dto.construction_year,
34 )?;
35
36 let created = self.repository.create(&building).await?;
37 Ok(self.to_response_dto(&created))
38 }
39
40 pub async fn get_building(&self, id: Uuid) -> Result<Option<BuildingResponseDto>, String> {
41 let building = self.repository.find_by_id(id).await?;
42 Ok(building.map(|b| self.to_response_dto(&b)))
43 }
44
45 pub async fn list_buildings(&self) -> Result<Vec<BuildingResponseDto>, String> {
46 let buildings = self.repository.find_all().await?;
47 Ok(buildings.iter().map(|b| self.to_response_dto(b)).collect())
48 }
49
50 pub async fn list_buildings_paginated(
51 &self,
52 page_request: &PageRequest,
53 organization_id: Option<Uuid>,
54 ) -> Result<(Vec<BuildingResponseDto>, i64), String> {
55 let filters = BuildingFilters {
56 organization_id,
57 ..Default::default()
58 };
59
60 let (buildings, total) = self
61 .repository
62 .find_all_paginated(page_request, &filters)
63 .await?;
64
65 let dtos = buildings.iter().map(|b| self.to_response_dto(b)).collect();
66 Ok((dtos, total))
67 }
68
69 pub async fn update_building(
70 &self,
71 id: Uuid,
72 dto: UpdateBuildingDto,
73 ) -> Result<BuildingResponseDto, String> {
74 let mut building = self
75 .repository
76 .find_by_id(id)
77 .await?
78 .ok_or_else(|| "Building not found".to_string())?;
79
80 building.update_info(dto.name, dto.address, dto.city, dto.postal_code);
81
82 let updated = self.repository.update(&building).await?;
83 Ok(self.to_response_dto(&updated))
84 }
85
86 pub async fn delete_building(&self, id: Uuid) -> Result<bool, String> {
87 self.repository.delete(id).await
88 }
89
90 fn to_response_dto(&self, building: &Building) -> BuildingResponseDto {
91 BuildingResponseDto {
92 id: building.id.to_string(),
93 name: building.name.clone(),
94 address: building.address.clone(),
95 city: building.city.clone(),
96 postal_code: building.postal_code.clone(),
97 country: building.country.clone(),
98 total_units: building.total_units,
99 construction_year: building.construction_year,
100 created_at: building.created_at.to_rfc3339(),
101 updated_at: building.updated_at.to_rfc3339(),
102 }
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109 use crate::application::ports::BuildingRepository;
110 use async_trait::async_trait;
111 use mockall::mock;
112
113 mock! {
114 BuildingRepo {}
115
116 #[async_trait]
117 impl BuildingRepository for BuildingRepo {
118 async fn create(&self, building: &Building) -> Result<Building, String>;
119 async fn find_by_id(&self, id: Uuid) -> Result<Option<Building>, String>;
120 async fn find_all(&self) -> Result<Vec<Building>, String>;
121 async fn find_all_paginated(
122 &self,
123 page_request: &PageRequest,
124 filters: &BuildingFilters,
125 ) -> Result<(Vec<Building>, i64), String>;
126 async fn update(&self, building: &Building) -> Result<Building, String>;
127 async fn delete(&self, id: Uuid) -> Result<bool, String>;
128 }
129 }
130
131 #[tokio::test]
132 async fn test_create_building_success() {
133 let mut mock_repo = MockBuildingRepo::new();
134
135 mock_repo.expect_create().returning(|b| Ok(b.clone()));
136
137 let use_cases = BuildingUseCases::new(Arc::new(mock_repo));
138
139 let dto = CreateBuildingDto {
140 organization_id: Uuid::new_v4().to_string(),
141 name: "Test Building".to_string(),
142 address: "123 Test St".to_string(),
143 city: "Paris".to_string(),
144 postal_code: "75001".to_string(),
145 country: "France".to_string(),
146 total_units: 10,
147 construction_year: Some(2000),
148 };
149
150 let result = use_cases.create_building(dto).await;
151 assert!(result.is_ok());
152 }
153
154 #[tokio::test]
155 async fn test_create_building_validation_fails() {
156 let mock_repo = MockBuildingRepo::new();
157 let use_cases = BuildingUseCases::new(Arc::new(mock_repo));
158
159 let dto = CreateBuildingDto {
160 organization_id: Uuid::new_v4().to_string(),
161 name: "".to_string(), address: "123 Test St".to_string(),
163 city: "Paris".to_string(),
164 postal_code: "75001".to_string(),
165 country: "France".to_string(),
166 total_units: 10,
167 construction_year: Some(2000),
168 };
169
170 let result = use_cases.create_building(dto).await;
171 assert!(result.is_err());
172 }
173}