dogy_backend_api/service/pets/
handlers.rs1use axum::{
2 extract::{Path, State},
3 Extension, Json,
4};
5use serde_json::{json, Value};
6use uuid::Uuid;
7
8use crate::{middleware::auth::layer::CurrentUser, AppState};
9
10use super::{
11 models::{
12 AllFullPet, FullPet, JoinedFullPet, PetAttributes, PetBase, UpdatePetAttributes,
13 UpdatePetBase,
14 },
15 store::retrieve_full_pet,
16};
17
18pub async fn create_pet(
19 Extension(current_user): Extension<CurrentUser>,
20 State(state): State<AppState>,
21 Json(mut pet): Json<FullPet>,
22) -> Json<FullPet> {
23 let conn = &*state.db;
24 let mut txn = conn.begin().await.unwrap();
25
26 let pet_id: (Uuid,) = sqlx::query_as(
28 r#"INSERT INTO pets (name, age, gender, size, photo_url, weight, weight_unit)
29 VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING id;"#,
30 )
31 .bind(&pet.base.name)
32 .bind(pet.base.age)
33 .bind(&pet.base.gender)
34 .bind(&pet.base.size)
35 .bind(&pet.base.photo_url)
36 .bind(pet.base.weight)
37 .bind(&pet.base.weight_unit)
38 .fetch_one(&mut *txn)
39 .await
40 .unwrap();
41
42 let pet_attrs_id: (Uuid,) = sqlx::query_as(
44 "INSERT INTO pet_attrs (pet_id, is_sterilized) VALUES ($1, $2) RETURNING id; ",
45 )
46 .bind(pet_id.0)
47 .bind(pet.attributes.sterilized)
48 .fetch_one(&mut *txn)
49 .await
50 .unwrap();
51
52 sqlx::query(
53 "INSERT INTO pet_attr_aggression_levels (pet_attr_id, aggression_levels) VALUES ($1, $2);",
54 )
55 .bind(pet_attrs_id.0)
56 .bind(&pet.attributes.aggression_levels)
57 .execute(&mut *txn)
58 .await
59 .unwrap();
60
61 sqlx::query("INSERT INTO pet_attr_allergies (pet_attr_id, allergies) VALUES ($1, $2);")
62 .bind(pet_attrs_id.0)
63 .bind(&pet.attributes.allergies)
64 .execute(&mut *txn)
65 .await
66 .unwrap();
67
68 sqlx::query("INSERT INTO pet_attr_behaviors (pet_attr_id, behaviors) VALUES ($1, $2);")
69 .bind(pet_attrs_id.0)
70 .bind(&pet.attributes.behaviors)
71 .execute(&mut *txn)
72 .await
73 .unwrap();
74
75 sqlx::query("INSERT INTO pet_attr_breeds (pet_attr_id, breeds) VALUES ($1, $2);")
76 .bind(pet_attrs_id.0)
77 .bind(&pet.attributes.breeds)
78 .execute(&mut *txn)
79 .await
80 .unwrap();
81
82 sqlx::query("INSERT INTO pet_attr_interactions (pet_attr_id, interactions) VALUES ($1, $2);")
83 .bind(pet_attrs_id.0)
84 .bind(&pet.attributes.interactions)
85 .execute(&mut *txn)
86 .await
87 .unwrap();
88
89 sqlx::query("INSERT INTO pet_attr_personalities (pet_attr_id, personalities) VALUES ($1, $2);")
90 .bind(pet_attrs_id.0)
91 .bind(&pet.attributes.personalities)
92 .execute(&mut *txn)
93 .await
94 .unwrap();
95
96 sqlx::query("INSERT INTO pet_attr_reactivities (pet_attr_id, reactivities) VALUES ($1, $2);")
97 .bind(pet_attrs_id.0)
98 .bind(&pet.attributes.reactivities)
99 .execute(&mut *txn)
100 .await
101 .unwrap();
102
103 sqlx::query(
104 "INSERT INTO users_pets_link
105 (pet_id, user_id, is_dog_owner, is_dog_sitter)
106 VALUES ($1, $2, TRUE, FALSE);",
107 )
108 .bind(pet_id.0)
109 .bind(current_user.internal_id.unwrap())
110 .execute(&mut *txn)
111 .await
112 .unwrap();
113
114 txn.commit().await.unwrap();
115
116 pet.base.pet_id = pet_id.0;
117
118 Json(pet)
119}
120
121pub async fn get_pet(
122 Extension(_current_user): Extension<CurrentUser>,
123 State(state): State<AppState>,
124 Path(pet_id): Path<Uuid>,
125) -> Json<FullPet> {
126 let conn = &*state.db;
127 let pet: FullPet = retrieve_full_pet(conn, pet_id).await;
128
129 Json(pet)
130}
131
132pub async fn get_all_pets(
133 Extension(current_user): Extension<CurrentUser>,
134 State(state): State<AppState>,
135) -> Json<AllFullPet> {
136 let conn = &*state.db;
137 let pets_row = sqlx::query_as::<_, JoinedFullPet>(
138 r#"SELECT p.*, attr.is_sterilized, attr_aggr.aggression_levels,
139 attr_all.allergies, attr_be.behaviors, attr_br.breeds,
140 attr_int.interactions, attr_pe.personalities, attr_re.reactivities
141 FROM pets p
142 LEFT JOIN users_pets_link upl ON p.id = upl.pet_id
143 LEFT JOIN pet_attrs attr ON p.id = attr.pet_id
144 LEFT JOIN pet_attr_aggression_levels attr_aggr ON attr.id = attr_aggr.pet_attr_id
145 LEFT JOIN pet_attr_allergies attr_all ON attr.id = attr_all.pet_attr_id
146 LEFT JOIN pet_attr_behaviors attr_be ON attr.id = attr_be.pet_attr_id
147 LEFT JOIN pet_attr_breeds attr_br ON attr.id = attr_br.pet_attr_id
148 LEFT JOIN pet_attr_interactions attr_int ON attr.id = attr_int.pet_attr_id
149 LEFT JOIN pet_attr_personalities attr_pe ON attr.id = attr_pe.pet_attr_id
150 LEFT JOIN pet_attr_reactivities attr_re ON attr.id = attr_re.pet_attr_id
151 WHERE upl.user_id = $1
152 "#,
153 )
154 .bind(current_user.internal_id.unwrap())
155 .fetch_all(conn)
156 .await
157 .unwrap();
158
159 let pets = pets_row
160 .into_iter()
161 .map(|pet| FullPet {
162 base: PetBase {
163 pet_id: pet.id,
164 name: pet.name,
165 age: pet.age,
166 gender: pet.gender,
167 size: pet.size,
168 photo_url: pet.photo_url,
169 weight: pet.weight,
170 weight_unit: pet.weight_unit,
171 },
172 attributes: PetAttributes {
173 aggression_levels: pet.aggression_levels,
174 allergies: pet.allergies,
175 breeds: pet.breeds,
176 behaviors: pet.behaviors,
177 interactions: pet.interactions,
178 personalities: pet.personalities,
179 reactivities: pet.reactivities,
180 sterilized: pet.is_sterilized,
181 },
182 })
183 .collect();
184
185 Json(AllFullPet { pets })
186}
187
188pub async fn delete_pet(
189 Extension(_current_user): Extension<CurrentUser>,
190 State(state): State<AppState>,
191 Path(pet_id): Path<Uuid>,
192) -> Json<Value> {
193 let conn = &*state.db;
194 sqlx::query("DELETE FROM pets WHERE id = $1;")
195 .bind(pet_id)
196 .execute(conn)
197 .await
198 .unwrap();
199
200 Json(json!({ "message": format!("Pet {} deleted successfully", pet_id) }))
201}
202
203pub async fn update_base_pet(
204 State(state): State<AppState>,
205 Path(pet_id): Path<Uuid>,
206 Json(pet): Json<UpdatePetBase>,
207) -> Json<UpdatePetBase> {
208 let conn = &*state.db;
209 sqlx::query(
210 r#"
211 UPDATE pets
212 SET
213 name = COALESCE($2, name),
214 age = COALESCE($3, age),
215 gender = COALESCE($4, gender),
216 size = COALESCE($5, size),
217 photo_url = COALESCE($6, photo_url),
218 weight = COALESCE($7, weight),
219 weight_unit = COALESCE($8, weight_unit)
220 WHERE id = $1;
221 "#,
222 )
223 .bind(pet_id)
224 .bind(&pet.name)
225 .bind(pet.age)
226 .bind(&pet.gender)
227 .bind(&pet.size)
228 .bind(&pet.photo_url)
229 .bind(pet.weight)
230 .bind(&pet.weight_unit)
231 .execute(conn)
232 .await
233 .unwrap();
234
235 Json(pet)
236}
237
238pub async fn update_pet_attributes(
239 State(state): State<AppState>,
240 Path(pet_id): Path<Uuid>,
241 Json(pet): Json<UpdatePetAttributes>,
242) -> Json<UpdatePetAttributes> {
243 let conn = &*state.db;
244 let mut txn = conn.begin().await.unwrap();
245 let attr_id = sqlx::query_as::<_, (Uuid,)>(
246 r#"SELECT pa.id FROM pet_attrs pa
247WHERE pa.pet_id = $1;"#,
248 )
249 .bind(pet_id)
250 .fetch_one(&mut *txn)
251 .await
252 .unwrap();
253
254 if let Some(aggression_levels) = &pet.aggression_levels {
255 sqlx::query(
256 r#"
257 UPDATE pet_attr_aggression_levels
258 SET
259 aggression_levels = $2
260 WHERE
261 pet_attr_id = $1;
262 "#,
263 )
264 .bind(attr_id.0)
265 .bind(aggression_levels)
266 .execute(&mut *txn)
267 .await
268 .unwrap();
269 }
270
271 if let Some(allergies) = &pet.allergies {
272 sqlx::query(
273 r#"
274 UPDATE pet_attr_allergies
275 SET
276 allergies = $2
277 WHERE
278 pet_attr_id = $1;
279 "#,
280 )
281 .bind(attr_id.0)
282 .bind(allergies)
283 .execute(&mut *txn)
284 .await
285 .unwrap();
286 }
287
288 if let Some(behaviors) = &pet.behaviors {
289 sqlx::query(
290 r#"
291 UPDATE pet_attr_behaviors
292 SET
293 behaviors = $2
294 WHERE
295 pet_attr_id = $1;
296 "#,
297 )
298 .bind(attr_id.0)
299 .bind(behaviors)
300 .execute(&mut *txn)
301 .await
302 .unwrap();
303 }
304
305 if let Some(breeds) = &pet.breeds {
306 sqlx::query(
307 r#"
308 UPDATE pet_attr_breeds
309 SET
310 breeds = $2
311 WHERE
312 pet_attr_id = $1;
313 "#,
314 )
315 .bind(attr_id.0)
316 .bind(breeds)
317 .execute(&mut *txn)
318 .await
319 .unwrap();
320 }
321
322 if let Some(interactions) = &pet.interactions {
323 sqlx::query(
324 r#"
325 UPDATE pet_attr_interactions
326 SET
327 interactions = $2
328 WHERE
329 pet_attr_id = $1;
330 "#,
331 )
332 .bind(attr_id.0)
333 .bind(interactions)
334 .execute(&mut *txn)
335 .await
336 .unwrap();
337 }
338
339 if let Some(personalities) = &pet.personalities {
340 sqlx::query(
341 r#"
342 UPDATE pet_attr_personalities
343 SET
344 personalities = $2
345 WHERE
346 pet_attr_id = $1;
347 "#,
348 )
349 .bind(attr_id.0)
350 .bind(personalities)
351 .execute(&mut *txn)
352 .await
353 .unwrap();
354 }
355
356 if let Some(reactivities) = &pet.reactivities {
357 sqlx::query(
358 r#"
359 UPDATE pet_attr_reactivities
360 SET
361 reactivities = $2
362 WHERE
363 pet_attr_id = $1;
364 "#,
365 )
366 .bind(attr_id.0)
367 .bind(reactivities)
368 .execute(&mut *txn)
369 .await
370 .unwrap();
371 }
372
373 if let Some(sterilized) = &pet.sterilized {
374 sqlx::query(
375 r#"
376 UPDATE pet_attrs
377 SET
378 is_sterilized = $2
379 WHERE
380 id = $1;
381 "#,
382 )
383 .bind(attr_id.0)
384 .bind(sterilized)
385 .execute(&mut *txn)
386 .await
387 .unwrap();
388 }
389
390 txn.commit().await.unwrap();
391
392 Json(pet)
393}