koprogo_api/infrastructure/web/handlers/
unit_handlers.rs1use crate::application::dto::{CreateUnitDto, PageRequest, PageResponse};
2use crate::infrastructure::audit::{AuditEventType, AuditLogEntry};
3use crate::infrastructure::web::{AppState, AuthenticatedUser};
4use actix_web::{get, post, put, web, HttpResponse, Responder};
5use uuid::Uuid;
6use validator::Validate;
7
8#[post("/units")]
9pub async fn create_unit(
10 state: web::Data<AppState>,
11 user: AuthenticatedUser, mut dto: web::Json<CreateUnitDto>,
13) -> impl Responder {
14 let organization_id = match user.require_organization() {
17 Ok(org_id) => org_id,
18 Err(e) => {
19 return HttpResponse::Unauthorized().json(serde_json::json!({
20 "error": e.to_string()
21 }))
22 }
23 };
24 dto.organization_id = organization_id.to_string();
25
26 if let Err(errors) = dto.validate() {
27 return HttpResponse::BadRequest().json(serde_json::json!({
28 "error": "Validation failed",
29 "details": errors.to_string()
30 }));
31 }
32
33 match state.unit_use_cases.create_unit(dto.into_inner()).await {
34 Ok(unit) => {
35 AuditLogEntry::new(
37 AuditEventType::UnitCreated,
38 Some(user.user_id),
39 Some(organization_id),
40 )
41 .with_resource("Unit", Uuid::parse_str(&unit.id).unwrap())
42 .log();
43
44 HttpResponse::Created().json(unit)
45 }
46 Err(err) => {
47 AuditLogEntry::new(
49 AuditEventType::UnitCreated,
50 Some(user.user_id),
51 Some(organization_id),
52 )
53 .with_error(err.clone())
54 .log();
55
56 HttpResponse::BadRequest().json(serde_json::json!({
57 "error": err
58 }))
59 }
60 }
61}
62
63#[get("/units/{id}")]
64pub async fn get_unit(state: web::Data<AppState>, id: web::Path<Uuid>) -> impl Responder {
65 match state.unit_use_cases.get_unit(*id).await {
66 Ok(Some(unit)) => HttpResponse::Ok().json(unit),
67 Ok(None) => HttpResponse::NotFound().json(serde_json::json!({
68 "error": "Unit not found"
69 })),
70 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({
71 "error": err
72 })),
73 }
74}
75
76#[get("/units")]
77pub async fn list_units(
78 state: web::Data<AppState>,
79 user: AuthenticatedUser,
80 page_request: web::Query<PageRequest>,
81) -> impl Responder {
82 let organization_id = user.organization_id;
83
84 match state
85 .unit_use_cases
86 .list_units_paginated(&page_request, organization_id)
87 .await
88 {
89 Ok((units, total)) => {
90 let response =
91 PageResponse::new(units, page_request.page, page_request.per_page, total);
92 HttpResponse::Ok().json(response)
93 }
94 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({
95 "error": err
96 })),
97 }
98}
99
100#[get("/buildings/{building_id}/units")]
101pub async fn list_units_by_building(
102 state: web::Data<AppState>,
103 building_id: web::Path<Uuid>,
104) -> impl Responder {
105 match state
106 .unit_use_cases
107 .list_units_by_building(*building_id)
108 .await
109 {
110 Ok(units) => HttpResponse::Ok().json(units),
111 Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({
112 "error": err
113 })),
114 }
115}
116
117#[put("/units/{unit_id}/assign-owner/{owner_id}")]
118pub async fn assign_owner(
119 state: web::Data<AppState>,
120 user: AuthenticatedUser,
121 path: web::Path<(Uuid, Uuid)>,
122) -> impl Responder {
123 let (unit_id, owner_id) = path.into_inner();
124
125 match state.unit_use_cases.assign_owner(unit_id, owner_id).await {
126 Ok(unit) => {
127 AuditLogEntry::new(
129 AuditEventType::UnitAssignedToOwner,
130 Some(user.user_id),
131 user.organization_id,
132 )
133 .with_resource("Unit", unit_id)
134 .log();
135
136 HttpResponse::Ok().json(unit)
137 }
138 Err(err) => {
139 AuditLogEntry::new(
141 AuditEventType::UnitAssignedToOwner,
142 Some(user.user_id),
143 user.organization_id,
144 )
145 .with_resource("Unit", unit_id)
146 .with_error(err.clone())
147 .log();
148
149 HttpResponse::BadRequest().json(serde_json::json!({
150 "error": err
151 }))
152 }
153 }
154}