Fix inventory api serialization issues

This commit is contained in:
Jeremy Wall 2022-11-19 18:32:36 -05:00
parent 2a1180dd94
commit a1b7f953bb
7 changed files with 76 additions and 33 deletions

View File

@ -1,14 +1,14 @@
{ {
"db": "SQLite", "db": "SQLite",
"0e06f6e072e2c55769feda0d5f998509139097fee640caf8fb38c7087669bee4": { "07f619ff4474e9eb5f4d56497abb724e6952b4e43d681ba5ecd61490cf990ae9": {
"describe": { "describe": {
"columns": [], "columns": [],
"nullable": [], "nullable": [],
"parameters": { "parameters": {
"Right": 4 "Right": 1
} }
}, },
"query": "insert into filtered_ingredients(user_id, name, form, measure_type)\n values (?, ?, ?, ?)" "query": "delete from filtered_ingredients where user_id = ?"
}, },
"104f07472670436d3eee1733578bbf0c92dc4f965d3d13f9bf4bfbc92958c5b6": { "104f07472670436d3eee1733578bbf0c92dc4f965d3d13f9bf4bfbc92958c5b6": {
"describe": { "describe": {
@ -92,16 +92,6 @@
}, },
"query": "insert into recipes (user_id, recipe_id, recipe_text) values (?, ?, ?)\n on conflict(user_id, recipe_id) do update set recipe_text=excluded.recipe_text" "query": "insert into recipes (user_id, recipe_id, recipe_text) values (?, ?, ?)\n on conflict(user_id, recipe_id) do update set recipe_text=excluded.recipe_text"
}, },
"512003bd6ef47567b243bbcaefcbd220acf36efab4cbd43b1a8debe59593577c": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Right": 5
}
},
"query": "insert into modified_amts(user_id, name, form, measure_type, amt)\n values (?, ?, ?, ?, ?)"
},
"5d743897fb0d8fd54c3708f1b1c6e416346201faa9e28823c1ba5a421472b1fa": { "5d743897fb0d8fd54c3708f1b1c6e416346201faa9e28823c1ba5a421472b1fa": {
"describe": { "describe": {
"columns": [], "columns": [],
@ -184,6 +174,16 @@
}, },
"query": "insert into sessions (id, session_value) values (?, ?)" "query": "insert into sessions (id, session_value) values (?, ?)"
}, },
"9e24ed2ea4d235e3a036025a0a0b5ea685546a81d7f2469a59a2fc1fc88798dc": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Right": 1
}
},
"query": "delete from modified_amts where user_id = ?"
},
"ad3408cd773dd8f9308255ec2800171638a1aeda9817c57fb8360f97115f8e97": { "ad3408cd773dd8f9308255ec2800171638a1aeda9817c57fb8360f97115f8e97": {
"describe": { "describe": {
"columns": [ "columns": [
@ -282,6 +282,16 @@
}, },
"query": "delete from sessions" "query": "delete from sessions"
}, },
"f510d7f8dcb79907abd8b17bd52127af1699b3b79d2737c7729972d6bd5a0693": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Right": 4
}
},
"query": "insert into filtered_ingredients(user_id, name, form, measure_type)\n values (?, ?, ?, ?) on conflict(user_id, name, form, measure_type) DO NOTHING"
},
"fc294739374d2a791214f747095e0bf9378989d1ff07d96a5431dbb208f21951": { "fc294739374d2a791214f747095e0bf9378989d1ff07d96a5431dbb208f21951": {
"describe": { "describe": {
"columns": [ "columns": [
@ -317,5 +327,15 @@
} }
}, },
"query": "select name, form, measure_type, amt from modified_amts where user_id = ?;" "query": "select name, form, measure_type, amt from modified_amts where user_id = ?;"
},
"fc9d3f8ce9d0b42f34307aeb31c128cc3267e77613d6f2e170c95e83d6e361df": {
"describe": {
"columns": [],
"nullable": [],
"parameters": {
"Right": 5
}
},
"query": "insert into modified_amts(user_id, name, form, measure_type, amt)\n values (?, ?, ?, ?, ?) on conflict (user_id, name, form, measure_type) do update set amt=excluded.amt"
} }
} }

View File

@ -1,4 +1,3 @@
use std::collections::BTreeMap;
// Copyright 2022 Jeremy Wall // Copyright 2022 Jeremy Wall
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
@ -12,9 +11,9 @@ use std::collections::BTreeMap;
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use std::net::SocketAddr;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use std::{collections::BTreeSet, net::SocketAddr};
use axum::{ use axum::{
body::{boxed, Full}, body::{boxed, Full},
@ -289,12 +288,14 @@ async fn api_save_inventory(
Extension(app_store): Extension<Arc<storage::SqliteStore>>, Extension(app_store): Extension<Arc<storage::SqliteStore>>,
session: storage::UserIdFromSession, session: storage::UserIdFromSession,
Json((filtered_ingredients, modified_amts)): Json<( Json((filtered_ingredients, modified_amts)): Json<(
BTreeSet<IngredientKey>, Vec<IngredientKey>,
BTreeMap<IngredientKey, String>, Vec<(IngredientKey, String)>,
)>, )>,
) -> impl IntoResponse { ) -> impl IntoResponse {
use storage::{UserId, UserIdFromSession::FoundUserId}; use storage::{UserId, UserIdFromSession::FoundUserId};
if let FoundUserId(UserId(id)) = session { if let FoundUserId(UserId(id)) = session {
let filtered_ingredients = filtered_ingredients.into_iter().collect();
let modified_amts = modified_amts.into_iter().collect();
if let Err(e) = app_store if let Err(e) = app_store
.save_inventory_data(id, filtered_ingredients, modified_amts) .save_inventory_data(id, filtered_ingredients, modified_amts)
.await .await

View File

@ -124,7 +124,7 @@ pub trait APIStore {
async fn fetch_inventory_data<S: AsRef<str> + Send>( async fn fetch_inventory_data<S: AsRef<str> + Send>(
&self, &self,
user_id: S, user_id: S,
) -> Result<(BTreeSet<IngredientKey>, BTreeMap<IngredientKey, String>)>; ) -> Result<(Vec<IngredientKey>, Vec<(IngredientKey, String)>)>;
async fn save_inventory_data<S: AsRef<str> + Send>( async fn save_inventory_data<S: AsRef<str> + Send>(
&self, &self,
@ -496,7 +496,7 @@ impl APIStore for SqliteStore {
async fn fetch_inventory_data<S: AsRef<str> + Send>( async fn fetch_inventory_data<S: AsRef<str> + Send>(
&self, &self,
user_id: S, user_id: S,
) -> Result<(BTreeSet<IngredientKey>, BTreeMap<IngredientKey, String>)> { ) -> Result<(Vec<IngredientKey>, Vec<(IngredientKey, String)>)> {
let user_id = user_id.as_ref(); let user_id = user_id.as_ref();
struct FilteredIngredientRow { struct FilteredIngredientRow {
name: String, name: String,
@ -510,9 +510,9 @@ impl APIStore for SqliteStore {
) )
.fetch_all(self.pool.as_ref()) .fetch_all(self.pool.as_ref())
.await?; .await?;
let mut filtered_ingredients = BTreeSet::new(); let mut filtered_ingredients = Vec::new();
for row in filtered_ingredient_rows { for row in filtered_ingredient_rows {
filtered_ingredients.insert(IngredientKey::new( filtered_ingredients.push(IngredientKey::new(
row.name, row.name,
if row.form.is_empty() { if row.form.is_empty() {
None None
@ -535,9 +535,9 @@ impl APIStore for SqliteStore {
) )
.fetch_all(self.pool.as_ref()) .fetch_all(self.pool.as_ref())
.await?; .await?;
let mut modified_amts = BTreeMap::new(); let mut modified_amts = Vec::new();
for row in modified_amt_rows { for row in modified_amt_rows {
modified_amts.insert( modified_amts.push((
IngredientKey::new( IngredientKey::new(
row.name, row.name,
if row.form.is_empty() { if row.form.is_empty() {
@ -548,7 +548,7 @@ impl APIStore for SqliteStore {
row.measure_type, row.measure_type,
), ),
row.amt, row.amt,
); ));
} }
Ok((filtered_ingredients, modified_amts)) Ok((filtered_ingredients, modified_amts))
} }
@ -561,6 +561,15 @@ impl APIStore for SqliteStore {
) -> Result<()> { ) -> Result<()> {
let user_id = user_id.as_ref(); let user_id = user_id.as_ref();
let mut transaction = self.pool.as_ref().begin().await?; let mut transaction = self.pool.as_ref().begin().await?;
sqlx::query!(
"delete from filtered_ingredients where user_id = ?",
user_id
)
.execute(&mut transaction)
.await?;
sqlx::query!("delete from modified_amts where user_id = ?", user_id)
.execute(&mut transaction)
.await?;
for key in filtered_ingredients { for key in filtered_ingredients {
let name = key.name(); let name = key.name();
let form = key.form(); let form = key.form();

View File

@ -1,2 +1,2 @@
insert into filtered_ingredients(user_id, name, form, measure_type) insert into filtered_ingredients(user_id, name, form, measure_type)
values (?, ?, ?, ?) values (?, ?, ?, ?) on conflict(user_id, name, form, measure_type) DO NOTHING

View File

@ -1,2 +1,2 @@
insert into modified_amts(user_id, name, form, measure_type, amt) insert into modified_amts(user_id, name, form, measure_type, amt)
values (?, ?, ?, ?, ?) values (?, ?, ?, ?, ?) on conflict (user_id, name, form, measure_type) do update set amt=excluded.amt

View File

@ -392,8 +392,14 @@ impl HttpStore {
Err(format!("Status: {}", resp.status()).into()) Err(format!("Status: {}", resp.status()).into())
} else { } else {
debug!("We got a valid response back"); debug!("We got a valid response back");
let inventory = resp.json().await.map_err(|e| format!("{}", e))?; let (filtered_ingredients, modified_amts): (
Ok(inventory) Vec<IngredientKey>,
Vec<(IngredientKey, String)>,
) = resp.json().await.map_err(|e| format!("{}", e))?;
Ok((
filtered_ingredients.into_iter().collect(),
modified_amts.into_iter().collect(),
))
} }
} }
@ -403,6 +409,8 @@ impl HttpStore {
modified_amts: BTreeMap<IngredientKey, String>, modified_amts: BTreeMap<IngredientKey, String>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let mut path = self.root.clone(); let mut path = self.root.clone();
let filtered_ingredients: Vec<IngredientKey> = filtered_ingredients.into_iter().collect();
let modified_amts: Vec<(IngredientKey, String)> = modified_amts.into_iter().collect();
let serialized_inventory = to_string(&(filtered_ingredients, modified_amts)) let serialized_inventory = to_string(&(filtered_ingredients, modified_amts))
.expect("Unable to encode plan as json"); .expect("Unable to encode plan as json");
path.push_str("/inventory"); path.push_str("/inventory");

View File

@ -15,7 +15,7 @@ use std::collections::{BTreeMap, BTreeSet};
use recipes::{Ingredient, IngredientKey}; use recipes::{Ingredient, IngredientKey};
use sycamore::{futures::spawn_local_scoped, prelude::*}; use sycamore::{futures::spawn_local_scoped, prelude::*};
use tracing::{debug, instrument}; use tracing::{debug, info, instrument};
fn make_ingredients_rows<'ctx, G: Html>( fn make_ingredients_rows<'ctx, G: Html>(
cx: Scope<'ctx>, cx: Scope<'ctx>,
@ -187,15 +187,20 @@ pub fn ShoppingList<G: Html>(cx: Scope) -> View<G> {
}); });
create_effect(cx, move || { create_effect(cx, move || {
save_click.track(); save_click.track();
info!("Registering save request for inventory");
spawn_local_scoped(cx, { spawn_local_scoped(cx, {
let state = crate::app_state::State::get_from_context(cx); let state = crate::app_state::State::get_from_context(cx);
let store = crate::api::HttpStore::get_from_context(cx); let store = crate::api::HttpStore::get_from_context(cx);
let filtered_ingredients = state.filtered_ingredients.get_untracked().as_ref().clone();
let modified_amts = state.get_current_modified_amts();
async move { async move {
debug!(
?filtered_ingredients,
?modified_amts,
"Attempting save for inventory"
);
store store
.save_inventory_data( .save_inventory_data(filtered_ingredients, modified_amts)
state.filtered_ingredients.get_untracked().as_ref().clone(),
state.get_current_modified_amts(),
)
.await .await
.expect("Unable to save inventory data"); .expect("Unable to save inventory data");
} }