fix(properties): correct property saving and fetching
This commit is contained in:
parent
7e288b3a82
commit
5a14111db7
4 changed files with 209 additions and 10 deletions
28
src/api.rs
28
src/api.rs
|
@ -151,4 +151,32 @@ pub async fn delete_property_by_url(
|
|||
HttpResponse::InternalServerError().body("Failed to delete property by URL")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
pub async fn get_selected_properties(
|
||||
db: web::Data<Arc<Mutex<Database>>>,
|
||||
url: web::Path<String>,
|
||||
) -> HttpResponse {
|
||||
let db = db.lock().await;
|
||||
match db.get_selected_properties(&url).await {
|
||||
Ok(properties) => HttpResponse::Ok().json(properties),
|
||||
Err(e) => HttpResponse::InternalServerError().body(e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
pub async fn add_selected_property(
|
||||
db: web::Data<Arc<Mutex<Database>>>,
|
||||
url: web::Path<String>,
|
||||
property: web::Json<String>,
|
||||
) -> HttpResponse {
|
||||
let url = url.into_inner();
|
||||
let property = property.into_inner();
|
||||
|
||||
let db = db.lock().await;
|
||||
match db.add_selected_property(&url, &property).await {
|
||||
Ok(_) => HttpResponse::Ok().finish(),
|
||||
Err(e) => HttpResponse::InternalServerError().body(e.to_string())
|
||||
}
|
||||
}
|
|
@ -47,7 +47,60 @@ pub async fn load_items_from_db(current_url: &str) -> Result<Vec<Item>, String>
|
|||
format!("Failed to parse items: {:?}", err)
|
||||
})?;
|
||||
log!("[DEBUG] Successfully parsed {} items", items.len());
|
||||
Ok(items)
|
||||
|
||||
// Get the selected properties for the current URL
|
||||
let selected_properties_response = gloo_net::http::Request::get(
|
||||
&format!("/api/urls/{}/properties", encoded_url)
|
||||
)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|err| {
|
||||
log!("[ERROR] Network error: {:?}", err);
|
||||
format!("Failed to fetch selected properties: {:?}", err)
|
||||
})?;
|
||||
if selected_properties_response.status() == 200 {
|
||||
let selected_properties: Vec<String> = selected_properties_response
|
||||
.json()
|
||||
.await
|
||||
.map_err(|err| {
|
||||
log!("[ERROR] JSON parsing error: {:?}", err);
|
||||
format!("Failed to parse selected properties: {:?}", err)
|
||||
})?;
|
||||
log!("[DEBUG] Successfully received selected properties");
|
||||
|
||||
// Filter the items to only include the selected properties
|
||||
let filtered_items = items
|
||||
.into_iter()
|
||||
.map(|item| {
|
||||
let filtered_custom_properties = item
|
||||
.custom_properties
|
||||
.into_iter()
|
||||
.filter(|(key, _)| selected_properties.contains(key))
|
||||
.collect();
|
||||
Item {
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
description: item.description,
|
||||
wikidata_id: item.wikidata_id,
|
||||
custom_properties: filtered_custom_properties,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Ok(filtered_items)
|
||||
} else {
|
||||
let body = selected_properties_response.text().await.unwrap_or_default();
|
||||
log!("[ERROR] Server error details:
|
||||
Status: {}
|
||||
URL: {}
|
||||
Response Body: {}
|
||||
Request URL: {}",
|
||||
selected_properties_response.status(),
|
||||
api_url,
|
||||
body,
|
||||
current_url
|
||||
);
|
||||
Err(format!("Server error ({}): {}", selected_properties_response.status(), body))
|
||||
}
|
||||
} else {
|
||||
let body = response.text().await.unwrap_or_default();
|
||||
log!("[ERROR] Server error details:
|
||||
|
@ -465,7 +518,44 @@ pub fn ItemsList(
|
|||
Arc::new(move |property: String| {
|
||||
// Normalize the property ID
|
||||
let normalized_property = property.replace("http://www.wikidata.org/prop/", "");
|
||||
|
||||
let normalized_property_clone = normalized_property.clone();
|
||||
|
||||
// Check if property is already selected
|
||||
if !selected_properties.get().contains_key(&normalized_property) && !normalized_property.is_empty() {
|
||||
// Add property to selected properties
|
||||
set_selected_properties.update(|selected| {
|
||||
selected.insert(normalized_property.clone(), true);
|
||||
});
|
||||
|
||||
// Save the selected property to the database
|
||||
spawn_local({
|
||||
let current_url = Rc::clone(¤t_url);
|
||||
let normalized_property = normalized_property_clone.clone();
|
||||
async move {
|
||||
let response = gloo_net::http::Request::post(
|
||||
&format!("/api/urls/{}/properties", encode(¤t_url))
|
||||
)
|
||||
.json(&normalized_property)
|
||||
.unwrap()
|
||||
.send()
|
||||
.await;
|
||||
|
||||
match response {
|
||||
Ok(resp) => {
|
||||
if resp.status() == 200 {
|
||||
log!("Property saved successfully");
|
||||
} else {
|
||||
log!("Error saving property: {}", resp.status_text());
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
log!("Error saving property: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
set_custom_properties.update(|props| {
|
||||
if !props.contains(&normalized_property) && !normalized_property.is_empty() {
|
||||
props.push(normalized_property.clone());
|
||||
|
|
64
src/db.rs
64
src/db.rs
|
@ -67,8 +67,22 @@ mod db_impl {
|
|||
eprintln!("Failed creating items table: {}", e);
|
||||
e
|
||||
})?;
|
||||
|
||||
// 4. Table for selected properties
|
||||
conn.execute_batch(
|
||||
"CREATE TABLE IF NOT EXISTS selected_properties (
|
||||
url_id INTEGER NOT NULL,
|
||||
property_id INTEGER NOT NULL,
|
||||
PRIMARY KEY (url_id, property_id),
|
||||
FOREIGN KEY (url_id) REFERENCES urls(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (property_id) REFERENCES properties(id) ON DELETE CASCADE
|
||||
);"
|
||||
).map_err(|e| {
|
||||
eprintln!("Failed creating properties table: {}", e);
|
||||
e
|
||||
})?;
|
||||
|
||||
// 4. Junction table for custom properties
|
||||
// 5. Junction table for custom properties
|
||||
conn.execute_batch(
|
||||
"CREATE TABLE IF NOT EXISTS item_properties (
|
||||
item_id TEXT NOT NULL,
|
||||
|
@ -373,6 +387,54 @@ mod db_impl {
|
|||
tx.commit()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn add_selected_property(&self, url: &str, property: &str) -> Result<(), Error> {
|
||||
let mut conn = self.conn.lock().await;
|
||||
let tx = conn.transaction()?;
|
||||
|
||||
// Get URL ID
|
||||
let url_id = tx.query_row(
|
||||
"SELECT id FROM urls WHERE url = ?",
|
||||
[url],
|
||||
|row| row.get::<_, i64>(0)
|
||||
)?;
|
||||
|
||||
// Get/Create property
|
||||
let prop_id = match tx.query_row(
|
||||
"SELECT id FROM properties WHERE name = ?",
|
||||
[property],
|
||||
|row| row.get::<_, i64>(0)
|
||||
) {
|
||||
Ok(id) => id,
|
||||
Err(_) => {
|
||||
tx.execute("INSERT INTO properties (name) VALUES (?)", [property])?;
|
||||
tx.last_insert_rowid()
|
||||
}
|
||||
};
|
||||
|
||||
// Insert into selected_properties
|
||||
tx.execute(
|
||||
"INSERT OR IGNORE INTO selected_properties (url_id, property_id) VALUES (?, ?)",
|
||||
[url_id, prop_id]
|
||||
)?;
|
||||
|
||||
tx.commit()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_selected_properties(&self, url: &str) -> Result<Vec<String>, Error> {
|
||||
let conn = self.conn.lock().await;
|
||||
let mut stmt = conn.prepare(
|
||||
"SELECT p.name
|
||||
FROM selected_properties sp
|
||||
JOIN properties p ON sp.property_id = p.id
|
||||
JOIN urls u ON sp.url_id = u.id
|
||||
WHERE u.url = ?"
|
||||
)?;
|
||||
|
||||
let properties = stmt.query_map([url], |row| row.get(0))?;
|
||||
properties.collect()
|
||||
}
|
||||
|
||||
// function to log database state
|
||||
pub async fn debug_dump(&self) -> Result<(), Error> {
|
||||
|
|
33
src/main.rs
33
src/main.rs
|
@ -3,7 +3,7 @@ use actix_web::{web, HttpResponse, Responder};
|
|||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
use compareware::db::Database;
|
||||
use compareware::api::{ItemRequest,create_item, get_items, delete_item_by_url};
|
||||
use compareware::api::{ItemRequest,create_item, get_items, get_selected_properties, add_selected_property};
|
||||
use compareware::models::item::Item;
|
||||
|
||||
#[actix_web::main]
|
||||
|
@ -14,7 +14,7 @@ async fn main() -> std::io::Result<()> {
|
|||
use leptos_actix::{generate_route_list, LeptosRoutes};
|
||||
use compareware::app::*;
|
||||
use compareware::db::Database;
|
||||
use compareware::api::{get_items, create_item, delete_item, delete_property}; // Import API handlers
|
||||
use compareware::api::{delete_item, delete_property}; // Import API handlers
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
|
@ -50,6 +50,8 @@ async fn main() -> std::io::Result<()> {
|
|||
.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)) // Delete item for URL
|
||||
.route("/properties", web::get().to(get_selected_properties_handler))
|
||||
.route("/properties", web::post().to(add_selected_property_handler))
|
||||
.route("/properties/{property}", web::delete().to(delete_property)) // Delete property for URL
|
||||
)
|
||||
)
|
||||
|
@ -98,13 +100,30 @@ async fn create_item_handler(
|
|||
create_item(db, web::Json(request)).await
|
||||
}
|
||||
|
||||
// Handler to delete an item for a specific URL
|
||||
async fn delete_item_handler(
|
||||
// // Handler to delete an item for a specific URL
|
||||
// async fn delete_item_handler(
|
||||
// db: web::Data<Arc<Mutex<Database>>>,
|
||||
// path: web::Path<(String, String)>,
|
||||
// ) -> impl Responder {
|
||||
// let (url, item_id) = path.into_inner();
|
||||
// delete_item_by_url(db, web::Path::from(url), web::Path::from(item_id)).await
|
||||
// }
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
async fn get_selected_properties_handler(
|
||||
db: web::Data<Arc<Mutex<Database>>>,
|
||||
path: web::Path<(String, String)>,
|
||||
url: web::Path<String>,
|
||||
) -> impl Responder {
|
||||
let (url, item_id) = path.into_inner();
|
||||
delete_item_by_url(db, web::Path::from(url), web::Path::from(item_id)).await
|
||||
get_selected_properties(db, url).await
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
async fn add_selected_property_handler(
|
||||
db: web::Data<Arc<Mutex<Database>>>,
|
||||
url: web::Path<String>,
|
||||
property: web::Json<String>,
|
||||
) -> impl Responder {
|
||||
add_selected_property(db, url, property).await
|
||||
}
|
||||
#[cfg(feature = "ssr")]
|
||||
// Define the index handler
|
||||
|
|
Loading…
Add table
Reference in a new issue