fix(db): fix property and item deletion

This commit is contained in:
ryan 2025-02-25 16:47:58 +03:00
parent 0a05b41ffa
commit a47d6b2e3a
4 changed files with 118 additions and 76 deletions

View file

@ -74,15 +74,16 @@ pub async fn create_item(
#[cfg(feature = "ssr")]
pub async fn delete_item(
db: web::Data<Arc<Mutex<Database>>>,
url: web::Query<String>,
item_id: web::Path<String>,
path: web::Path<(String, String)>, // (url, item_id)
) -> HttpResponse {
let (url, item_id) = path.into_inner();
log!("[API] Deleting item {} from URL {}", item_id, url);
let db = db.lock().await;
match db.delete_item_by_url(&url, &item_id).await {
Ok(_) => HttpResponse::Ok().body("Item deleted"),
Err(err) => {
leptos::logging::error!("Failed to delete item: {:?}", err);
HttpResponse::InternalServerError().body("Failed to delete item")
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => {
log!("[API] Delete error: {:?}", e);
HttpResponse::InternalServerError().body(e.to_string())
}
}
}
@ -90,15 +91,16 @@ pub async fn delete_item(
#[cfg(feature = "ssr")]
pub async fn delete_property(
db: web::Data<Arc<Mutex<Database>>>,
url: web::Query<String>,
property: web::Path<String>,
path: web::Path<(String, String)>, // (url, property)
) -> HttpResponse {
let (url, property) = path.into_inner();
log!("[API] Deleting property {} from URL {}", property, url);
let db = db.lock().await;
match db.delete_property_by_url(&url, &property).await {
Ok(_) => HttpResponse::Ok().body("Property deleted"),
Err(err) => {
leptos::logging::error!("Failed to delete property: {:?}", err);
HttpResponse::InternalServerError().body("Failed to delete property")
Ok(_) => HttpResponse::Ok().finish(),
Err(e) => {
log!("[API] Delete error: {:?}", e);
HttpResponse::InternalServerError().body(e.to_string())
}
}
}

View file

@ -223,57 +223,75 @@ pub fn ItemsList(
}
}
let current_url_for_remove_item = Rc::clone(&current_url);
// Function to remove an item
let remove_item = move |index: usize| {
let item_id = items.get()[index].id.clone();
spawn_local(async move {
let response = gloo_net::http::Request::delete(&format!("/api/items/{}", item_id))
let remove_item = {
let set_items = set_items.clone();
move |index: usize| {
let item_id = items.get()[index].id.clone();
let current_url = Rc::clone(&current_url_for_remove_item);
spawn_local(async move {
let response = gloo_net::http::Request::delete(
&format!("/api/urls/{}/items/{}", encode(&current_url), item_id)
)
.send()
.await;
match response {
Ok(resp) => {
if resp.status() == 200 {
set_items.update(|items| {
items.remove(index);
});
log!("Item deleted: {}", item_id);
} else {
log!("Failed to delete item: {}", resp.status_text());
match response {
Ok(resp) => {
if resp.status() == 200 {
set_items.update(|items| {
items.remove(index);
});
log!("Item deleted: {}", item_id);
} else {
log!("Failed to delete item: {}", resp.status_text());
}
}
Err(err) => log!("Failed to delete item: {:?}", err),
}
Err(err) => log!("Failed to delete item: {:?}", err),
}
});
});
}
};
let current_url_for_remove_property = Rc::clone(&current_url);
// Function to remove a property
let remove_property = move |property: String| {
spawn_local(async move {
let response = gloo_net::http::Request::delete(&format!("/api/properties/{}", property))
let remove_property = {
let set_custom_properties = set_custom_properties.clone();
let set_selected_properties = set_selected_properties.clone();
let set_items = set_items.clone();
move |property: String| {
let current_url = Rc::clone(&current_url_for_remove_property);
spawn_local(async move {
let response = gloo_net::http::Request::delete(
&format!("/api/urls/{}/properties/{}", encode(&current_url), property)
)
.send()
.await;
match response {
Ok(resp) => {
if resp.status() == 200 {
set_custom_properties.update(|props| {
props.retain(|p| p != &property);
});
set_selected_properties.update(|selected| {
selected.remove(&property);
});
set_items.update(|items| {
for item in items {
item.custom_properties.remove(&property);
}
});
log!("Property deleted: {}", property);
} else {
log!("Failed to delete property: {}", resp.status_text());
match response {
Ok(resp) => {
if resp.status() == 200 {
set_custom_properties.update(|props| {
props.retain(|p| p != &property);
});
set_selected_properties.update(|selected| {
selected.remove(&property);
});
set_items.update(|items| {
for item in items {
item.custom_properties.remove(&property);
}
});
log!("Property deleted: {}", property);
} else {
log!("Failed to delete property: {}", resp.status_text());
}
}
Err(err) => log!("Failed to delete property: {:?}", err),
}
Err(err) => log!("Failed to delete property: {:?}", err),
}
});
});
}
};
// State to store Wikidata suggestions
@ -596,9 +614,10 @@ pub fn ItemsList(
<tr>
<th>{ "Property" }</th>
{move || items.get().iter().enumerate().map(|(index, item)| {
let remove_item = remove_item.clone();
view! {
<th>
{ item.name.clone() }
{item.name.clone()}
<button on:click=move |_| remove_item(index)>{ "Delete" }</button>
</th>
}
@ -753,7 +772,9 @@ pub fn ItemsList(
move || {
let update_item = Arc::clone(&update_item_outer);
let custom_props = custom_properties.get().clone();
let remove_property = remove_property.clone();
custom_props.into_iter().map(move |property| {
let remove_property_clone = remove_property.clone();
let update_item_inner = Arc::clone(&update_item);
let normalized_property = property.replace("http://www.wikidata.org/prop/", "");
let property_label = property_labels.get().get(&normalized_property).cloned().unwrap_or_else(|| normalized_property.clone());
@ -765,7 +786,7 @@ pub fn ItemsList(
{ property_label }
<button class="delete-property" on:click=move |_| {
log!("Deleting property: {}", property_clone_for_button);
remove_property(property_clone_for_button.clone());
remove_property_clone(property_clone_for_button.clone());
set_custom_properties.update(|props| {
props.retain(|p| p != &property_clone_for_button);
});

View file

@ -274,7 +274,7 @@ mod db_impl {
JOIN properties p ON ip.property_id = p.id
WHERE ip.item_id = ?"
)?;
let mapped_rows = stmt.query_map([&item.id], |row| {
Ok((row.get::<_, String>(0)?, row.get::<_, String>(1)?))
})?;
@ -326,35 +326,54 @@ mod db_impl {
// Delete an item from the database for a specific URL
pub async fn delete_item_by_url(&self, url: &str, item_id: &str) -> Result<(), Error> {
let conn = self.conn.lock().await;
let url_id: i64 = conn.query_row("SELECT id FROM urls WHERE url = ?", &[url], |row| row.get(0))?;
conn.execute("DELETE FROM items WHERE id = ? AND url_id = ?", &[item_id, &url_id.to_string()])?;
logging::log!("Item deleted from the database for URL: {}", url);
let mut conn = self.conn.lock().await;
let tx = conn.transaction()?;
// Get URL ID
let url_id: i64 = tx.query_row(
"SELECT id FROM urls WHERE url = ?",
[url],
|row| row.get(0)
)?;
// Delete item and properties
tx.execute(
"DELETE FROM items WHERE id = ? AND url_id = ?",
[item_id, &url_id.to_string()],
)?;
tx.commit()?;
Ok(())
}
// Delete a property from the database for a specific URL
pub async fn delete_property_by_url(&self, url: &str, property: &str) -> Result<(), Error> {
let conn = self.conn.lock().await;
let url_id: i64 = conn.query_row("SELECT id FROM urls WHERE url = ?", &[url], |row| row.get(0))?;
let mut conn = self.conn.lock().await;
let tx = conn.transaction()?;
// Delete from junction table instead of JSON
conn.execute(
"DELETE FROM item_properties
WHERE property_id IN (
SELECT id FROM properties WHERE name = ?
) AND item_id IN (
SELECT id FROM items WHERE url_id = ?
)",
rusqlite::params![
property, // &str
url_id // i64
],
// Get URL ID
let url_id: i64 = tx.query_row(
"SELECT id FROM urls WHERE url = ?",
[url],
|row| row.get(0)
)?;
logging::log!("Property deleted from the database for URL: {}", url);
// Delete property from all items in this URL
tx.execute(
"DELETE FROM item_properties
WHERE property_id IN (
SELECT id FROM properties WHERE name = ?
)
AND item_id IN (
SELECT id FROM items WHERE url_id = ?
)",
[property, &url_id.to_string()],
)?;
tx.commit()?;
Ok(())
}
// function to log database state
pub async fn debug_dump(&self) -> Result<(), Error> {
let conn = self.conn.lock().await;

View file

@ -49,9 +49,9 @@ async fn main() -> std::io::Result<()> {
web::scope("/urls/{url}")
.route("/items", web::get().to(get_items_handler)) // GET items by URL
.route("/items", web::post().to(create_item_handler)) // Create item for URL
.route("/items/{item_id}", web::delete().to(delete_item_handler)) // Delete item
.route("/items/{item_id}", web::delete().to(delete_item)) // Delete item for URL
.route("/properties/{property}", web::delete().to(delete_property)) // Delete property for URL
)
.route("/properties/{property}", web::delete().to(delete_property)), // DELETE /api/properties/{property}
)
// Register server functions
.route("/api/{tail:.*}", leptos_actix::handle_server_fns())