dogy_backend_api/service/pets/
handlers.rs

1use 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    // Inserting Base Pet
27    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    // Inserting Pet Attributes
43    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}