list archive
This commit is contained in:
+139
-2
@@ -442,8 +442,9 @@ pub struct SqliteListRepository;
|
||||
impl ListRepository for SqliteListRepository {
|
||||
async fn list_summaries(&self, txn: &mut SqliteConnection) -> DomainResult<Vec<GroceryList>> {
|
||||
let rows = sqlx::query(
|
||||
"SELECT l.id, l.name, l.revision
|
||||
"SELECT l.id, l.name, l.revision, l.created_at, l.archived_at
|
||||
FROM lists l
|
||||
WHERE l.archived_at IS NULL
|
||||
ORDER BY l.created_at DESC",
|
||||
)
|
||||
.fetch_all(&mut *txn)
|
||||
@@ -455,6 +456,33 @@ impl ListRepository for SqliteListRepository {
|
||||
id: row.get(0),
|
||||
name: row.get(1),
|
||||
revision: row.get(2),
|
||||
created_at: row.get(3),
|
||||
archived_at: row.get(4),
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
async fn list_archived_summaries(
|
||||
&self,
|
||||
txn: &mut SqliteConnection,
|
||||
) -> DomainResult<Vec<GroceryList>> {
|
||||
let rows = sqlx::query(
|
||||
"SELECT l.id, l.name, l.revision, l.created_at, l.archived_at
|
||||
FROM lists l
|
||||
WHERE l.archived_at IS NOT NULL
|
||||
ORDER BY l.archived_at DESC, l.created_at DESC",
|
||||
)
|
||||
.fetch_all(&mut *txn)
|
||||
.await
|
||||
.map_err(db_error)?;
|
||||
Ok(rows
|
||||
.into_iter()
|
||||
.map(|row| GroceryList {
|
||||
id: row.get(0),
|
||||
name: row.get(1),
|
||||
revision: row.get(2),
|
||||
created_at: row.get(3),
|
||||
archived_at: row.get(4),
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
@@ -479,6 +507,8 @@ impl ListRepository for SqliteListRepository {
|
||||
id: list_id,
|
||||
name,
|
||||
revision: 0,
|
||||
created_at: now(),
|
||||
archived_at: None,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -488,7 +518,7 @@ impl ListRepository for SqliteListRepository {
|
||||
list_id: i64,
|
||||
) -> DomainResult<Option<GroceryList>> {
|
||||
let row = sqlx::query(
|
||||
"SELECT l.id, l.name, l.revision
|
||||
"SELECT l.id, l.name, l.revision, l.created_at, l.archived_at
|
||||
FROM lists l
|
||||
WHERE l.id = ?1",
|
||||
)
|
||||
@@ -500,8 +530,36 @@ impl ListRepository for SqliteListRepository {
|
||||
id: row.get(0),
|
||||
name: row.get(1),
|
||||
revision: row.get(2),
|
||||
created_at: row.get(3),
|
||||
archived_at: row.get(4),
|
||||
}))
|
||||
}
|
||||
|
||||
async fn set_archived(
|
||||
&self,
|
||||
txn: &mut SqliteConnection,
|
||||
list_id: i64,
|
||||
archived: bool,
|
||||
) -> DomainResult<()> {
|
||||
let result = if archived {
|
||||
sqlx::query("UPDATE lists SET archived_at = ?1 WHERE id = ?2")
|
||||
.bind(now())
|
||||
.bind(list_id)
|
||||
.execute(&mut *txn)
|
||||
.await
|
||||
.map_err(db_error)?
|
||||
} else {
|
||||
sqlx::query("UPDATE lists SET archived_at = NULL WHERE id = ?1")
|
||||
.bind(list_id)
|
||||
.execute(&mut *txn)
|
||||
.await
|
||||
.map_err(db_error)?
|
||||
};
|
||||
if result.rows_affected() == 0 {
|
||||
return Err(DomainError::NotFound);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
@@ -1608,6 +1666,85 @@ mod tests {
|
||||
assert_eq!(ids, vec![first.id, second.id]);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn archiving_a_list_hides_it_from_summaries() {
|
||||
let db = setup().await;
|
||||
let list = create_list(&db, "Weekly shop").await;
|
||||
let lists = SqliteListRepository;
|
||||
|
||||
let list_id = list.id;
|
||||
db.run(move |txn| {
|
||||
let lists = lists.clone();
|
||||
Box::pin(async move { lists.set_archived(txn, list_id, true).await })
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let summaries = db
|
||||
.run(move |txn| {
|
||||
let lists = lists.clone();
|
||||
Box::pin(async move { lists.list_summaries(txn).await })
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(summaries.is_empty());
|
||||
|
||||
let archived = db
|
||||
.run(move |txn| {
|
||||
let lists = lists.clone();
|
||||
Box::pin(async move { lists.list_archived_summaries(txn).await })
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(archived.len(), 1);
|
||||
assert_eq!(archived[0].id, list.id);
|
||||
assert!(archived[0].archived_at.is_some());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn unarchiving_a_list_restores_it_to_summaries() {
|
||||
let db = setup().await;
|
||||
let list = create_list(&db, "Weekly shop").await;
|
||||
let lists = SqliteListRepository;
|
||||
|
||||
let list_id = list.id;
|
||||
db.run(move |txn| {
|
||||
let lists = lists.clone();
|
||||
Box::pin(async move { lists.set_archived(txn, list_id, true).await })
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
db.run(move |txn| {
|
||||
let lists = lists.clone();
|
||||
Box::pin(async move { lists.set_archived(txn, list_id, false).await })
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let summaries = db
|
||||
.run(move |txn| {
|
||||
let lists = lists.clone();
|
||||
Box::pin(async move { lists.list_summaries(txn).await })
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(summaries.len(), 1);
|
||||
assert!(summaries[0].archived_at.is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn archiving_a_missing_list_fails() {
|
||||
let db = setup().await;
|
||||
let lists = SqliteListRepository;
|
||||
let result = db
|
||||
.run(move |txn| {
|
||||
let lists = lists.clone();
|
||||
Box::pin(async move { lists.set_archived(txn, 9999, true).await })
|
||||
})
|
||||
.await;
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_list_returns_list_or_none() {
|
||||
let db = setup().await;
|
||||
|
||||
Reference in New Issue
Block a user