From 8bcd9440bf50d8740719f4fd68136ab721711559 Mon Sep 17 00:00:00 2001 From: Cameron Cordes Date: Sun, 19 Mar 2023 12:13:04 -0400 Subject: [PATCH] Add Endpoint for Batch add/removal of tags on a file --- src/main.rs | 1 + src/tags.rs | 51 ++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/main.rs b/src/main.rs index 81f9f7d..f340a01 100644 --- a/src/main.rs +++ b/src/main.rs @@ -489,6 +489,7 @@ fn main() -> std::io::Result<()> { .route(web::delete().to(remove_tagged_photo::)), ) .service(web::resource("image/tags/all").route(web::get().to(get_all_tags::))) + .service(web::resource("image/tags/batch").route(web::post().to(update_tags::))) .app_data(app_data.clone()) .app_data::>>(Data::new(Mutex::new(user_dao))) .app_data::>>>(Data::new(Mutex::new(Box::new( diff --git a/src/tags.rs b/src/tags.rs index b69ee67..44df9b4 100644 --- a/src/tags.rs +++ b/src/tags.rs @@ -5,7 +5,7 @@ use chrono::Utc; use diesel::prelude::*; use log::{debug, info}; use schema::{tagged_photo, tags}; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use std::borrow::BorrowMut; use std::sync::Mutex; @@ -70,7 +70,42 @@ pub async fn remove_tagged_photo( .into_http_internal_err() } -#[derive(Serialize, Queryable, Clone, Debug)] +pub async fn update_tags(_: Claims, tag_dao: web::Data>, request: web::Json) -> impl Responder { + let mut dao = tag_dao.lock() + .expect("Unable to get TagDao"); + + return dao.get_tags_for_path(&request.file_name) + .and_then(|existing_tags| { + dao.get_all_tags().map(|all| (existing_tags, all)) + }) + .and_then(|(existing_tags, all_tags)| { + let tags_to_remove = existing_tags.iter() + .filter(|&t| !request.tag_ids.contains(&t.id)) + .collect::>(); + + for tag in tags_to_remove { + info!("Removing tag {:?} from file: {:?}", tag.name, request.file_name); + dao.remove_tag(&tag.name, &request.file_name) + .expect(&format!("Unable to remove tag {:?}", &tag.name)); + } + + let new_tags = all_tags.iter() + .filter(|&t| !existing_tags.contains(t) && request.tag_ids.contains(&t.id)) + .collect::>(); + + for new_tag in new_tags { + info!("Adding tag {:?} to file: {:?}", new_tag.name, request.file_name); + + dao.tag_file(&request.file_name, new_tag.id) + .with_context(|| format!("Unable to tag file {:?} with tag: {:?}", request.file_name, new_tag.name)) + .unwrap(); + } + + Ok(HttpResponse::Ok()) + }).into_http_internal_err() +} + +#[derive(Serialize, Queryable, Clone, Debug, PartialEq)] pub struct Tag { pub id: i32, pub name: String, @@ -100,6 +135,12 @@ pub struct TaggedPhoto { pub created_time: i64, } +#[derive(Debug, Deserialize)] +pub struct AddTagsRequest { + pub file_name: String, + pub tag_ids: Vec, +} + pub trait TagDao { fn get_all_tags(&mut self) -> anyhow::Result>; fn get_tags_for_path(&mut self, path: &str) -> anyhow::Result>; @@ -184,9 +225,9 @@ impl TagDao for SqliteTagDao { .filter(tagged_photo::tag_id.eq(tag.id)) .filter(tagged_photo::photo_name.eq(path)), ) - .execute(&mut self.connection) - .with_context(|| format!("Unable to delete tag: '{}'", &tag.name)) - .map(|_| Some(())) + .execute(&mut self.connection) + .with_context(|| format!("Unable to delete tag: '{}'", &tag.name)) + .map(|_| Some(())) } else { info!("No tag found with name '{}'", tag_name); Ok(None)