refactor(cafes,cups): remove notes field from Cafe and Cup entities
- Add migrations 0014 and 0015 to drop notes columns - Remove notes from domain structs, repositories, views, CLI, routes - Remove notes textarea/columns from web templates - Update bootstrap script, tests, backup module, and README
This commit is contained in:
parent
23817ff95a
commit
ae3c1090de
11 changed files with 29 additions and 80 deletions
|
|
@ -124,7 +124,7 @@ brewlog bag <cmd> Manage bags of coffee
|
||||||
brewlog gear <cmd> Manage brewing gear (grinders, brewers, filter papers)
|
brewlog gear <cmd> Manage brewing gear (grinders, brewers, filter papers)
|
||||||
brewlog brew <cmd> Manage brews (add, list, get, delete — no update)
|
brewlog brew <cmd> Manage brews (add, list, get, delete — no update)
|
||||||
brewlog cafe <cmd> Manage cafes
|
brewlog cafe <cmd> Manage cafes
|
||||||
brewlog cup <cmd> Manage cups (tasting notes with ratings)
|
brewlog cup <cmd> Manage cups (cafe visits with ratings)
|
||||||
brewlog token <cmd> Manage API tokens (create, list, revoke)
|
brewlog token <cmd> Manage API tokens (create, list, revoke)
|
||||||
brewlog backup Export all data to JSON on stdout
|
brewlog backup Export all data to JSON on stdout
|
||||||
brewlog restore --file F Restore data from a JSON backup into an empty database
|
brewlog restore --file F Restore data from a JSON backup into an empty database
|
||||||
|
|
|
||||||
1
migrations/0015_remove_cup_notes.sql
Normal file
1
migrations/0015_remove_cup_notes.sql
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
ALTER TABLE cups DROP COLUMN notes;
|
||||||
|
|
@ -515,129 +515,128 @@ fi
|
||||||
./target/debug/brewlog cup add \
|
./target/debug/brewlog cup add \
|
||||||
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Red Brick Espresso") | .id')" \
|
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Red Brick Espresso") | .id')" \
|
||||||
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Monmouth Coffee") | .id')" \
|
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Monmouth Coffee") | .id')" \
|
||||||
--notes "Rich and balanced; the house espresso blend is a crowd-pleaser." \
|
|
||||||
--rating 4
|
--rating 4
|
||||||
|
|
||||||
./target/debug/brewlog cup add \
|
./target/debug/brewlog cup add \
|
||||||
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Kamwangi") | .id')" \
|
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Kamwangi") | .id')" \
|
||||||
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Monmouth Coffee") | .id')" \
|
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Monmouth Coffee") | .id')" \
|
||||||
--notes "Juicy Kenyan pour-over, bright blackcurrant notes." \
|
|
||||||
--rating 5
|
--rating 5
|
||||||
|
|
||||||
# Prufrock Coffee (London) - 3 cups
|
# Prufrock Coffee (London) - 3 cups
|
||||||
./target/debug/brewlog cup add \
|
./target/debug/brewlog cup add \
|
||||||
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Kochere") | .id')" \
|
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Kochere") | .id')" \
|
||||||
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Prufrock Coffee") | .id')" \
|
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Prufrock Coffee") | .id')" \
|
||||||
--notes "Delicate floral Ethiopian; exceptional barista technique." \
|
|
||||||
--rating 5
|
--rating 5
|
||||||
|
|
||||||
./target/debug/brewlog cup add \
|
./target/debug/brewlog cup add \
|
||||||
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="La Laja") | .id')" \
|
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="La Laja") | .id')" \
|
||||||
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Prufrock Coffee") | .id')" \
|
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Prufrock Coffee") | .id')" \
|
||||||
--notes "Interesting Mexican natural, lots of chocolate." \
|
|
||||||
--rating 4
|
--rating 4
|
||||||
|
|
||||||
./target/debug/brewlog cup add \
|
./target/debug/brewlog cup add \
|
||||||
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Ben Saïd Natural") | .id')" \
|
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Ben Saïd Natural") | .id')" \
|
||||||
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Prufrock Coffee") | .id')" \
|
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Prufrock Coffee") | .id')" \
|
||||||
--notes "Guest roast from Tim Wendelboe; bergamot hit was incredible." \
|
|
||||||
--rating 5
|
--rating 5
|
||||||
|
|
||||||
# Hola Coffee (Madrid) - 2 cups
|
# Hola Coffee (Madrid) - 2 cups
|
||||||
./target/debug/brewlog cup add \
|
./target/debug/brewlog cup add \
|
||||||
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Cerro Azul") | .id')" \
|
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Cerro Azul") | .id')" \
|
||||||
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Hola Coffee") | .id')" \
|
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Hola Coffee") | .id')" \
|
||||||
--notes "Clean Colombian washed, plum and grapefruit forward." \
|
|
||||||
--rating 4
|
--rating 4
|
||||||
|
|
||||||
./target/debug/brewlog cup add \
|
./target/debug/brewlog cup add \
|
||||||
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Guji Highland") | .id')" \
|
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Guji Highland") | .id')" \
|
||||||
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Hola Coffee") | .id')" \
|
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Hola Coffee") | .id')" \
|
||||||
--notes "Peachy natural from April Coffee, worked well as espresso." \
|
|
||||||
--rating 3
|
--rating 3
|
||||||
|
|
||||||
# Toma Café (Madrid) - 1 cup
|
# Toma Café (Madrid) - 1 cup
|
||||||
./target/debug/brewlog cup add \
|
./target/debug/brewlog cup add \
|
||||||
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Daterra Sweet Collection") | .id')" \
|
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Daterra Sweet Collection") | .id')" \
|
||||||
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Toma Café") | .id')" \
|
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Toma Café") | .id')"
|
||||||
--notes "Smooth Brazilian, hazelnut and chocolate. Nice afternoon flat white."
|
|
||||||
|
|
||||||
# The Barn (Berlin) - 3 cups
|
# The Barn (Berlin) - 3 cups
|
||||||
./target/debug/brewlog cup add \
|
./target/debug/brewlog cup add \
|
||||||
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Gatomboya") | .id')" \
|
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Gatomboya") | .id')" \
|
||||||
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="The Barn") | .id')" \
|
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="The Barn") | .id')" \
|
||||||
--notes "Their own roast; classic Kenyan brightness with tomato acidity." \
|
|
||||||
--rating 4
|
--rating 4
|
||||||
|
|
||||||
./target/debug/brewlog cup add \
|
./target/debug/brewlog cup add \
|
||||||
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Los Pirineos") | .id')" \
|
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Los Pirineos") | .id')" \
|
||||||
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="The Barn") | .id')" \
|
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="The Barn") | .id')" \
|
||||||
--notes "Honey-processed El Salvador, maple sweetness." \
|
|
||||||
--rating 4
|
--rating 4
|
||||||
|
|
||||||
./target/debug/brewlog cup add \
|
./target/debug/brewlog cup add \
|
||||||
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Halo Beriti") | .id')" \
|
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Halo Beriti") | .id')" \
|
||||||
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="The Barn") | .id')" \
|
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="The Barn") | .id')" \
|
||||||
--notes "La Cabra guest roast, jasmine and lemon." \
|
|
||||||
--rating 5
|
--rating 5
|
||||||
|
|
||||||
# Companion Coffee (Berlin) - 2 cups
|
# Companion Coffee (Berlin) - 2 cups
|
||||||
./target/debug/brewlog cup add \
|
./target/debug/brewlog cup add \
|
||||||
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="El Paraiso 92 Anaerobic") | .id')" \
|
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="El Paraiso 92 Anaerobic") | .id')" \
|
||||||
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Companion Coffee") | .id')" \
|
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Companion Coffee") | .id')" \
|
||||||
--notes "Wild anaerobic from Dak; passionfruit explosion." \
|
|
||||||
--rating 5
|
--rating 5
|
||||||
|
|
||||||
./target/debug/brewlog cup add \
|
./target/debug/brewlog cup add \
|
||||||
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Oreti SL28") | .id')" \
|
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Oreti SL28") | .id')" \
|
||||||
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Companion Coffee") | .id')" \
|
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Companion Coffee") | .id')" \
|
||||||
--notes "Crisp Kenyan SL28, grapefruit and plum." \
|
|
||||||
--rating 4
|
--rating 4
|
||||||
|
|
||||||
# Man Versus Machine (Munich) - 2 cups
|
# Man Versus Machine (Munich) - 2 cups
|
||||||
./target/debug/brewlog cup add \
|
./target/debug/brewlog cup add \
|
||||||
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Finca Tamana Washed") | .id')" \
|
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Finca Tamana Washed") | .id')" \
|
||||||
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Man Versus Machine") | .id')" \
|
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Man Versus Machine") | .id')" \
|
||||||
--notes "Clean Colombian, red apple and caramel. Beautiful V60." \
|
|
||||||
--rating 4
|
--rating 4
|
||||||
|
|
||||||
./target/debug/brewlog cup add \
|
./target/debug/brewlog cup add \
|
||||||
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="La Esmeralda Geisha") | .id')" \
|
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="La Esmeralda Geisha") | .id')" \
|
||||||
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Man Versus Machine") | .id')" \
|
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Man Versus Machine") | .id')" \
|
||||||
--notes "Incredible Geisha from Friedhats; jasmine and papaya. Worth every penny." \
|
|
||||||
--rating 5
|
--rating 5
|
||||||
|
|
||||||
# Vits der Kaffee (Munich) - 1 cup
|
# Vits der Kaffee (Munich) - 1 cup
|
||||||
./target/debug/brewlog cup add \
|
./target/debug/brewlog cup add \
|
||||||
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="San Fermin") | .id')" \
|
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="San Fermin") | .id')" \
|
||||||
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Vits der Kaffee") | .id')" \
|
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Vits der Kaffee") | .id')" \
|
||||||
--notes "Origin Coffee guest roast; fruity Colombian, blood orange finish." \
|
|
||||||
--rating 3
|
--rating 3
|
||||||
|
|
||||||
# Full Court Press (Bristol) - 2 cups
|
# Full Court Press (Bristol) - 2 cups
|
||||||
./target/debug/brewlog cup add \
|
./target/debug/brewlog cup add \
|
||||||
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Suke Quto") | .id')" \
|
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Suke Quto") | .id')" \
|
||||||
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Full Court Press") | .id')" \
|
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Full Court Press") | .id')" \
|
||||||
--notes "Natural Ethiopian from Stow Coffee; blackberry and jasmine." \
|
|
||||||
--rating 5
|
--rating 5
|
||||||
|
|
||||||
./target/debug/brewlog cup add \
|
./target/debug/brewlog cup add \
|
||||||
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Death to Decaf") | .id')" \
|
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Death to Decaf") | .id')" \
|
||||||
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Full Court Press") | .id')" \
|
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Full Court Press") | .id')" \
|
||||||
--notes "Surprisingly good decaf from Dark Arts. Cocoa and cherry." \
|
|
||||||
--rating 3
|
--rating 3
|
||||||
|
|
||||||
# Small Street Espresso (Bristol) - 2 cups
|
# Small Street Espresso (Bristol) - 2 cups
|
||||||
./target/debug/brewlog cup add \
|
./target/debug/brewlog cup add \
|
||||||
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Simbi") | .id')" \
|
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="Simbi") | .id')" \
|
||||||
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Small Street Espresso") | .id')" \
|
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Small Street Espresso") | .id')" \
|
||||||
--notes "Rwandan washed from Bows; clean black tea and orange." \
|
|
||||||
--rating 4
|
--rating 4
|
||||||
|
|
||||||
./target/debug/brewlog cup add \
|
./target/debug/brewlog cup add \
|
||||||
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="La Chumeca") | .id')" \
|
--roast-id "$(./target/debug/brewlog roast list | jq -r '.[] | select(.name=="La Chumeca") | .id')" \
|
||||||
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Small Street Espresso") | .id')" \
|
--cafe-id "$(./target/debug/brewlog cafe list | jq -r '.[] | select(.name=="Small Street Espresso") | .id')" \
|
||||||
--notes "Costa Rican honey from Bows; sweet mandarin and honeycomb." \
|
|
||||||
--rating 4
|
--rating 4
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,7 @@ pub(crate) async fn update_cup(
|
||||||
Path(id): Path<CupId>,
|
Path(id): Path<CupId>,
|
||||||
Json(payload): Json<UpdateCup>,
|
Json(payload): Json<UpdateCup>,
|
||||||
) -> Result<Json<Cup>, ApiError> {
|
) -> Result<Json<Cup>, ApiError> {
|
||||||
let has_changes = payload.notes.is_some() || payload.rating.is_some();
|
let has_changes = payload.rating.is_some();
|
||||||
|
|
||||||
if !has_changes {
|
if !has_changes {
|
||||||
return Err(AppError::validation("no changes provided").into());
|
return Err(AppError::validation("no changes provided").into());
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ pub struct Cup {
|
||||||
pub id: CupId,
|
pub id: CupId,
|
||||||
pub roast_id: RoastId,
|
pub roast_id: RoastId,
|
||||||
pub cafe_id: CafeId,
|
pub cafe_id: CafeId,
|
||||||
pub notes: Option<String>,
|
|
||||||
pub rating: Option<i32>,
|
pub rating: Option<i32>,
|
||||||
pub created_at: DateTime<Utc>,
|
pub created_at: DateTime<Utc>,
|
||||||
pub updated_at: DateTime<Utc>,
|
pub updated_at: DateTime<Utc>,
|
||||||
|
|
@ -31,13 +30,11 @@ pub struct CupWithDetails {
|
||||||
pub struct NewCup {
|
pub struct NewCup {
|
||||||
pub roast_id: RoastId,
|
pub roast_id: RoastId,
|
||||||
pub cafe_id: CafeId,
|
pub cafe_id: CafeId,
|
||||||
pub notes: Option<String>,
|
|
||||||
pub rating: Option<i32>,
|
pub rating: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||||
pub struct UpdateCup {
|
pub struct UpdateCup {
|
||||||
pub notes: Option<String>,
|
|
||||||
pub rating: Option<i32>,
|
pub rating: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ use crate::infrastructure::database::DatabasePool;
|
||||||
|
|
||||||
const BASE_SELECT: &str = r"
|
const BASE_SELECT: &str = r"
|
||||||
SELECT
|
SELECT
|
||||||
c.id, c.roast_id, c.cafe_id, c.notes, c.rating,
|
c.id, c.roast_id, c.cafe_id, c.rating,
|
||||||
c.created_at, c.updated_at,
|
c.created_at, c.updated_at,
|
||||||
r.name as roast_name, r.slug as roast_slug,
|
r.name as roast_name, r.slug as roast_slug,
|
||||||
rr.name as roaster_name, rr.slug as roaster_slug,
|
rr.name as roaster_name, rr.slug as roaster_slug,
|
||||||
|
|
@ -57,7 +57,6 @@ impl SqlCupRepository {
|
||||||
id: CupId::new(record.id),
|
id: CupId::new(record.id),
|
||||||
roast_id: RoastId::new(record.roast_id),
|
roast_id: RoastId::new(record.roast_id),
|
||||||
cafe_id: CafeId::new(record.cafe_id),
|
cafe_id: CafeId::new(record.cafe_id),
|
||||||
notes: record.notes,
|
|
||||||
rating: record.rating,
|
rating: record.rating,
|
||||||
created_at: record.created_at,
|
created_at: record.created_at,
|
||||||
updated_at: record.updated_at,
|
updated_at: record.updated_at,
|
||||||
|
|
@ -70,7 +69,6 @@ impl SqlCupRepository {
|
||||||
id: CupId::new(record.id),
|
id: CupId::new(record.id),
|
||||||
roast_id: RoastId::new(record.roast_id),
|
roast_id: RoastId::new(record.roast_id),
|
||||||
cafe_id: CafeId::new(record.cafe_id),
|
cafe_id: CafeId::new(record.cafe_id),
|
||||||
notes: record.notes,
|
|
||||||
rating: record.rating,
|
rating: record.rating,
|
||||||
created_at: record.created_at,
|
created_at: record.created_at,
|
||||||
updated_at: record.updated_at,
|
updated_at: record.updated_at,
|
||||||
|
|
@ -141,12 +139,11 @@ impl CupRepository for SqlCupRepository {
|
||||||
.map_err(|err| RepositoryError::unexpected(err.to_string()))?;
|
.map_err(|err| RepositoryError::unexpected(err.to_string()))?;
|
||||||
|
|
||||||
let record = query_as::<_, CupRecord>(
|
let record = query_as::<_, CupRecord>(
|
||||||
"INSERT INTO cups (roast_id, cafe_id, notes, rating) VALUES (?, ?, ?, ?) \
|
"INSERT INTO cups (roast_id, cafe_id, rating) VALUES (?, ?, ?) \
|
||||||
RETURNING id, roast_id, cafe_id, notes, rating, created_at, updated_at",
|
RETURNING id, roast_id, cafe_id, rating, created_at, updated_at",
|
||||||
)
|
)
|
||||||
.bind(new_cup.roast_id.into_inner())
|
.bind(new_cup.roast_id.into_inner())
|
||||||
.bind(new_cup.cafe_id.into_inner())
|
.bind(new_cup.cafe_id.into_inner())
|
||||||
.bind(new_cup.notes.as_deref())
|
|
||||||
.bind(new_cup.rating)
|
.bind(new_cup.rating)
|
||||||
.fetch_one(&mut *tx)
|
.fetch_one(&mut *tx)
|
||||||
.await
|
.await
|
||||||
|
|
@ -196,7 +193,7 @@ impl CupRepository for SqlCupRepository {
|
||||||
|
|
||||||
async fn get(&self, id: CupId) -> Result<Cup, RepositoryError> {
|
async fn get(&self, id: CupId) -> Result<Cup, RepositoryError> {
|
||||||
let record = query_as::<_, CupRecord>(
|
let record = query_as::<_, CupRecord>(
|
||||||
"SELECT id, roast_id, cafe_id, notes, rating, created_at, updated_at FROM cups WHERE id = ?",
|
"SELECT id, roast_id, cafe_id, rating, created_at, updated_at FROM cups WHERE id = ?",
|
||||||
)
|
)
|
||||||
.bind(i64::from(id))
|
.bind(i64::from(id))
|
||||||
.fetch_optional(&self.pool)
|
.fetch_optional(&self.pool)
|
||||||
|
|
@ -268,7 +265,6 @@ impl CupRepository for SqlCupRepository {
|
||||||
let mut builder = QueryBuilder::new("UPDATE cups SET updated_at = CURRENT_TIMESTAMP");
|
let mut builder = QueryBuilder::new("UPDATE cups SET updated_at = CURRENT_TIMESTAMP");
|
||||||
let mut sep = true;
|
let mut sep = true;
|
||||||
|
|
||||||
push_update_field!(builder, sep, "notes", changes.notes);
|
|
||||||
push_update_field!(builder, sep, "rating", changes.rating);
|
push_update_field!(builder, sep, "rating", changes.rating);
|
||||||
let _ = sep;
|
let _ = sep;
|
||||||
|
|
||||||
|
|
@ -308,7 +304,6 @@ struct CupRecord {
|
||||||
id: i64,
|
id: i64,
|
||||||
roast_id: i64,
|
roast_id: i64,
|
||||||
cafe_id: i64,
|
cafe_id: i64,
|
||||||
notes: Option<String>,
|
|
||||||
rating: Option<i32>,
|
rating: Option<i32>,
|
||||||
created_at: DateTime<Utc>,
|
created_at: DateTime<Utc>,
|
||||||
updated_at: DateTime<Utc>,
|
updated_at: DateTime<Utc>,
|
||||||
|
|
@ -319,7 +314,6 @@ struct CupWithDetailsRecord {
|
||||||
id: i64,
|
id: i64,
|
||||||
roast_id: i64,
|
roast_id: i64,
|
||||||
cafe_id: i64,
|
cafe_id: i64,
|
||||||
notes: Option<String>,
|
|
||||||
rating: Option<i32>,
|
rating: Option<i32>,
|
||||||
created_at: DateTime<Utc>,
|
created_at: DateTime<Utc>,
|
||||||
updated_at: DateTime<Utc>,
|
updated_at: DateTime<Utc>,
|
||||||
|
|
|
||||||
|
|
@ -38,8 +38,6 @@ pub struct AddCupCommand {
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub cafe_id: i64,
|
pub cafe_id: i64,
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub notes: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
pub rating: Option<i32>,
|
pub rating: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -47,7 +45,6 @@ pub async fn add_cup(client: &BrewlogClient, command: AddCupCommand) -> Result<(
|
||||||
let payload = NewCup {
|
let payload = NewCup {
|
||||||
roast_id: RoastId::new(command.roast_id),
|
roast_id: RoastId::new(command.roast_id),
|
||||||
cafe_id: CafeId::new(command.cafe_id),
|
cafe_id: CafeId::new(command.cafe_id),
|
||||||
notes: command.notes,
|
|
||||||
rating: command.rating,
|
rating: command.rating,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -67,14 +64,11 @@ pub struct UpdateCupCommand {
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub notes: Option<String>,
|
|
||||||
#[arg(long)]
|
|
||||||
pub rating: Option<i32>,
|
pub rating: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_cup(client: &BrewlogClient, command: UpdateCupCommand) -> Result<()> {
|
pub async fn update_cup(client: &BrewlogClient, command: UpdateCupCommand) -> Result<()> {
|
||||||
let payload = UpdateCup {
|
let payload = UpdateCup {
|
||||||
notes: command.notes,
|
|
||||||
rating: command.rating,
|
rating: command.rating,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,6 @@ pub struct CupView {
|
||||||
pub roaster_slug: String,
|
pub roaster_slug: String,
|
||||||
pub cafe_name: String,
|
pub cafe_name: String,
|
||||||
pub cafe_slug: String,
|
pub cafe_slug: String,
|
||||||
pub notes: String,
|
|
||||||
pub has_notes: bool,
|
|
||||||
pub rating: String,
|
pub rating: String,
|
||||||
pub has_rating: bool,
|
pub has_rating: bool,
|
||||||
pub created_at: String,
|
pub created_at: String,
|
||||||
|
|
@ -18,8 +16,6 @@ pub struct CupView {
|
||||||
|
|
||||||
impl CupView {
|
impl CupView {
|
||||||
pub fn from_domain(cup: CupWithDetails) -> Self {
|
pub fn from_domain(cup: CupWithDetails) -> Self {
|
||||||
let notes = cup.cup.notes.clone().unwrap_or_default();
|
|
||||||
let has_notes = !notes.is_empty();
|
|
||||||
let rating = cup
|
let rating = cup
|
||||||
.cup
|
.cup
|
||||||
.rating
|
.rating
|
||||||
|
|
@ -34,8 +30,6 @@ impl CupView {
|
||||||
roaster_slug: cup.roaster_slug,
|
roaster_slug: cup.roaster_slug,
|
||||||
cafe_name: cup.cafe_name,
|
cafe_name: cup.cafe_name,
|
||||||
cafe_slug: cup.cafe_slug,
|
cafe_slug: cup.cafe_slug,
|
||||||
notes,
|
|
||||||
has_notes,
|
|
||||||
rating,
|
rating,
|
||||||
has_rating,
|
has_rating,
|
||||||
created_at: cup.cup.created_at.format("%Y-%m-%d %H:%M").to_string(),
|
created_at: cup.cup.created_at.format("%Y-%m-%d %H:%M").to_string(),
|
||||||
|
|
|
||||||
|
|
@ -70,15 +70,6 @@
|
||||||
<option value="1">1 - Poor</option>
|
<option value="1">1 - Poor</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
<label class="sm:col-span-2 flex flex-col gap-1 text-sm">
|
|
||||||
<span class="text-stone-700">Notes</span>
|
|
||||||
<textarea
|
|
||||||
name="notes"
|
|
||||||
rows="2"
|
|
||||||
class="input-field"
|
|
||||||
placeholder="Tasting impressions, preparation style..."
|
|
||||||
></textarea>
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center justify-end gap-2">
|
<div class="flex items-center justify-end gap-2">
|
||||||
<button
|
<button
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@
|
||||||
{% call table::sortable_header("Coffee", "roast", navigator, "#cup-list") %}
|
{% call table::sortable_header("Coffee", "roast", navigator, "#cup-list") %}
|
||||||
{% call table::sortable_header("Cafe", "cafe", navigator, "#cup-list") %}
|
{% call table::sortable_header("Cafe", "cafe", navigator, "#cup-list") %}
|
||||||
{% call table::sortable_header("Rating", "rating", navigator, "#cup-list") %}
|
{% call table::sortable_header("Rating", "rating", navigator, "#cup-list") %}
|
||||||
<th scope="col" class="mobile-hidden px-4 py-3">Notes</th>
|
|
||||||
<th scope="col" class="px-4 py-3 text-right">Actions</th>
|
<th scope="col" class="px-4 py-3 text-right">Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
@ -52,9 +51,6 @@
|
||||||
>
|
>
|
||||||
</td>
|
</td>
|
||||||
<td data-label="Rating" class="px-4 py-3 whitespace-nowrap">{{ cup.rating }}</td>
|
<td data-label="Rating" class="px-4 py-3 whitespace-nowrap">{{ cup.rating }}</td>
|
||||||
<td data-label="Notes" class="mobile-hidden px-4 py-3 text-sm text-stone-600">
|
|
||||||
{% if cup.has_notes %}{{ cup.notes }}{% else %}—{% endif %}
|
|
||||||
</td>
|
|
||||||
<td data-label="" class="px-4 py-3 text-right">
|
<td data-label="" class="px-4 py-3 text-right">
|
||||||
{% if is_authenticated %}
|
{% if is_authenticated %}
|
||||||
<div class="inline-flex items-center gap-1">
|
<div class="inline-flex items-center gap-1">
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ async fn creating_a_cup_returns_a_201_for_valid_data() {
|
||||||
let new_cup = NewCup {
|
let new_cup = NewCup {
|
||||||
roast_id: roast.id,
|
roast_id: roast.id,
|
||||||
cafe_id: cafe.id,
|
cafe_id: cafe.id,
|
||||||
notes: Some("Excellent pour-over".to_string()),
|
|
||||||
rating: Some(5),
|
rating: Some(5),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -33,7 +32,6 @@ async fn creating_a_cup_returns_a_201_for_valid_data() {
|
||||||
let cup: Cup = response.json().await.expect("Failed to parse response");
|
let cup: Cup = response.json().await.expect("Failed to parse response");
|
||||||
assert_eq!(cup.roast_id, roast.id);
|
assert_eq!(cup.roast_id, roast.id);
|
||||||
assert_eq!(cup.cafe_id, cafe.id);
|
assert_eq!(cup.cafe_id, cafe.id);
|
||||||
assert_eq!(cup.notes, Some("Excellent pour-over".to_string()));
|
|
||||||
assert_eq!(cup.rating, Some(5));
|
assert_eq!(cup.rating, Some(5));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,7 +47,6 @@ async fn creating_a_cup_without_optional_fields_returns_a_201() {
|
||||||
let new_cup = NewCup {
|
let new_cup = NewCup {
|
||||||
roast_id: roast.id,
|
roast_id: roast.id,
|
||||||
cafe_id: cafe.id,
|
cafe_id: cafe.id,
|
||||||
notes: None,
|
|
||||||
rating: None,
|
rating: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -64,7 +61,6 @@ async fn creating_a_cup_without_optional_fields_returns_a_201() {
|
||||||
assert_eq!(response.status(), 201);
|
assert_eq!(response.status(), 201);
|
||||||
|
|
||||||
let cup: Cup = response.json().await.expect("Failed to parse response");
|
let cup: Cup = response.json().await.expect("Failed to parse response");
|
||||||
assert_eq!(cup.notes, None);
|
|
||||||
assert_eq!(cup.rating, None);
|
assert_eq!(cup.rating, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,7 +72,6 @@ async fn creating_a_cup_requires_authentication() {
|
||||||
let new_cup = NewCup {
|
let new_cup = NewCup {
|
||||||
roast_id: RoastId::new(1),
|
roast_id: RoastId::new(1),
|
||||||
cafe_id: CafeId::new(1),
|
cafe_id: CafeId::new(1),
|
||||||
notes: None,
|
|
||||||
rating: None,
|
rating: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -119,7 +114,6 @@ async fn listing_cups_returns_a_200_with_enriched_data() {
|
||||||
let new_cup = NewCup {
|
let new_cup = NewCup {
|
||||||
roast_id: roast.id,
|
roast_id: roast.id,
|
||||||
cafe_id: cafe.id,
|
cafe_id: cafe.id,
|
||||||
notes: None,
|
|
||||||
rating: Some(4),
|
rating: Some(4),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -158,7 +152,6 @@ async fn getting_a_cup_returns_a_200_with_details() {
|
||||||
let new_cup = NewCup {
|
let new_cup = NewCup {
|
||||||
roast_id: roast.id,
|
roast_id: roast.id,
|
||||||
cafe_id: cafe.id,
|
cafe_id: cafe.id,
|
||||||
notes: Some("Great".to_string()),
|
|
||||||
rating: Some(3),
|
rating: Some(3),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -215,7 +208,6 @@ async fn updating_a_cup_returns_a_200_for_valid_data() {
|
||||||
let new_cup = NewCup {
|
let new_cup = NewCup {
|
||||||
roast_id: roast.id,
|
roast_id: roast.id,
|
||||||
cafe_id: cafe.id,
|
cafe_id: cafe.id,
|
||||||
notes: None,
|
|
||||||
rating: None,
|
rating: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -232,10 +224,7 @@ async fn updating_a_cup_returns_a_200_for_valid_data() {
|
||||||
.await
|
.await
|
||||||
.expect("Failed to parse response");
|
.expect("Failed to parse response");
|
||||||
|
|
||||||
let update = UpdateCup {
|
let update = UpdateCup { rating: Some(4) };
|
||||||
notes: Some("Updated notes".to_string()),
|
|
||||||
rating: Some(4),
|
|
||||||
};
|
|
||||||
|
|
||||||
let response = client
|
let response = client
|
||||||
.put(app.api_url(&format!("/cups/{}", cup.id)))
|
.put(app.api_url(&format!("/cups/{}", cup.id)))
|
||||||
|
|
@ -248,7 +237,6 @@ async fn updating_a_cup_returns_a_200_for_valid_data() {
|
||||||
assert_eq!(response.status(), 200);
|
assert_eq!(response.status(), 200);
|
||||||
|
|
||||||
let updated: Cup = response.json().await.expect("Failed to parse response");
|
let updated: Cup = response.json().await.expect("Failed to parse response");
|
||||||
assert_eq!(updated.notes, Some("Updated notes".to_string()));
|
|
||||||
assert_eq!(updated.rating, Some(4));
|
assert_eq!(updated.rating, Some(4));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -264,7 +252,6 @@ async fn updating_a_cup_with_no_changes_returns_a_400() {
|
||||||
let new_cup = NewCup {
|
let new_cup = NewCup {
|
||||||
roast_id: roast.id,
|
roast_id: roast.id,
|
||||||
cafe_id: cafe.id,
|
cafe_id: cafe.id,
|
||||||
notes: None,
|
|
||||||
rating: None,
|
rating: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -281,10 +268,7 @@ async fn updating_a_cup_with_no_changes_returns_a_400() {
|
||||||
.await
|
.await
|
||||||
.expect("Failed to parse response");
|
.expect("Failed to parse response");
|
||||||
|
|
||||||
let update = UpdateCup {
|
let update = UpdateCup { rating: None };
|
||||||
notes: None,
|
|
||||||
rating: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let response = client
|
let response = client
|
||||||
.put(app.api_url(&format!("/cups/{}", cup.id)))
|
.put(app.api_url(&format!("/cups/{}", cup.id)))
|
||||||
|
|
@ -309,7 +293,6 @@ async fn deleting_a_cup_returns_a_204_for_valid_id() {
|
||||||
let new_cup = NewCup {
|
let new_cup = NewCup {
|
||||||
roast_id: roast.id,
|
roast_id: roast.id,
|
||||||
cafe_id: cafe.id,
|
cafe_id: cafe.id,
|
||||||
notes: None,
|
|
||||||
rating: None,
|
rating: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue