diff --git a/.gitignore b/.gitignore index fedaa2b..611d252 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target .env +upvoters diff --git a/db/migrations/002_create_slug_to_title.sql b/db/migrations/002_create_slug_to_title.sql new file mode 100644 index 0000000..0e2fbe0 --- /dev/null +++ b/db/migrations/002_create_slug_to_title.sql @@ -0,0 +1,7 @@ +CREATE EXTENSION IF NOT EXISTS pgcrypto; + +CREATE TABLE IF NOT EXISTS titles ( + slug text not null, + title text not null, + primary key (slug) +); diff --git a/src/votes/handlers.rs b/src/votes/handlers.rs index 21c15ed..d2ba163 100644 --- a/src/votes/handlers.rs +++ b/src/votes/handlers.rs @@ -13,6 +13,7 @@ use uuid::Uuid; use crate::{ state::AppState, votes::{ + model::VoteRequest, repository::{delete_vote, get_top_n_slugs, insert_new_vote}, service::get_votes_and_voted, }, @@ -51,10 +52,12 @@ async fn upvote_handler( jar: CookieJar, Path(slug): Path, State(state): State, + Json(upvote_payload): Json, ) -> impl IntoResponse { let (jar, voter_id) = get_or_init_voter_id(jar); - match insert_new_vote(&slug, &voter_id, &state.db).await { + info!("{:?}", upvote_payload); + match insert_new_vote(&slug, &voter_id, upvote_payload.title, &state.db).await { Ok(()) => { info!(slug = %slug, voter_id = %voter_id, "upvoted successfully"); (StatusCode::OK, jar, "Successfully upvoted") @@ -260,7 +263,7 @@ mod tests { let slug = "delete_vote_handler_removes_vote_for_cookie_voter"; let voter_id = Uuid::from_u128(0xdef); delete_votes_for_slug(slug, &db).await.unwrap(); - insert_new_vote(slug, &voter_id, &db).await.unwrap(); + insert_new_vote(slug, &voter_id, None, &db).await.unwrap(); let app = crate::app(test_state(db.clone())); let request = Request::builder() @@ -309,8 +312,10 @@ mod tests { let voter_id = Uuid::from_u128(0x123); let other_voter_id = Uuid::from_u128(0x456); delete_votes_for_slug(slug, &db).await.unwrap(); - insert_new_vote(slug, &voter_id, &db).await.unwrap(); - insert_new_vote(slug, &other_voter_id, &db).await.unwrap(); + insert_new_vote(slug, &voter_id, None, &db).await.unwrap(); + insert_new_vote(slug, &other_voter_id, None, &db) + .await + .unwrap(); let app = crate::app(test_state(db.clone())); let request = Request::builder() @@ -337,7 +342,7 @@ mod tests { let slug = "get_votes_and_voted_handler_sets_cookie_for_new_voter"; let existing_voter_id = Uuid::from_u128(0x789); delete_votes_for_slug(slug, &db).await.unwrap(); - insert_new_vote(slug, &existing_voter_id, &db) + insert_new_vote(slug, &existing_voter_id, None, &db) .await .unwrap(); @@ -378,12 +383,16 @@ mod tests { Uuid::from_u128(0x101), Uuid::from_u128(0x102), ] { - insert_new_vote(slugs[0], &voter_id, &db).await.unwrap(); + insert_new_vote(slugs[0], &voter_id, None, &db) + .await + .unwrap(); } for voter_id in [Uuid::from_u128(0x200), Uuid::from_u128(0x201)] { - insert_new_vote(slugs[1], &voter_id, &db).await.unwrap(); + insert_new_vote(slugs[1], &voter_id, None, &db) + .await + .unwrap(); } - insert_new_vote(slugs[2], &Uuid::from_u128(0x300), &db) + insert_new_vote(slugs[2], &Uuid::from_u128(0x300), None, &db) .await .unwrap(); diff --git a/src/votes/model.rs b/src/votes/model.rs index 4993c5d..149271a 100644 --- a/src/votes/model.rs +++ b/src/votes/model.rs @@ -3,6 +3,12 @@ use serde::{Deserialize, Serialize}; use sqlx::prelude::FromRow; use uuid::Uuid; +/// Struct representing a vote request +#[derive(Debug, Deserialize)] +pub struct VoteRequest { + pub title: Option, +} + /// Struct representing a single vote (row) in the votes table #[derive(Debug, FromRow)] pub struct Vote { @@ -16,4 +22,5 @@ pub struct Vote { pub struct BestSlugs { pub slug: String, pub vote_count: i64, + pub title: String, } diff --git a/src/votes/repository.rs b/src/votes/repository.rs index 98be0ce..ff69179 100644 --- a/src/votes/repository.rs +++ b/src/votes/repository.rs @@ -19,7 +19,12 @@ pub async fn vote_exists(slug: &str, voter_id: &Uuid, db: &PgPool) -> Result 0) } -pub async fn insert_new_vote(slug: &str, voter_id: &Uuid, db: &PgPool) -> Result<()> { +pub async fn insert_new_vote( + slug: &str, + voter_id: &Uuid, + title: Option, + db: &PgPool, +) -> Result<()> { query( r#"insert into votes (slug, voter_id) values ($1, $2) @@ -29,6 +34,14 @@ pub async fn insert_new_vote(slug: &str, voter_id: &Uuid, db: &PgPool) -> Result .bind(voter_id) .execute(db) .await?; + + if let Some(title) = title { + query(r#"insert into titles values ($1, $2)"#) + .bind(slug) + .bind(title) + .execute(db) + .await?; + } Ok(()) } @@ -43,9 +56,9 @@ pub async fn get_vote_count_for_slug(slug: &str, db: &PgPool) -> Result { pub async fn get_top_n_slugs(n: i64, db: &PgPool) -> Result> { if n > 0 { let top_slugs = query_as::<_, BestSlugs>( - r#"select slug, count(*) AS vote_count - from votes - group by slug + r#"select slug, title, count(*) AS vote_count + from votes natural join titles + group by slug, title order by vote_count desc limit $1 "#, @@ -130,7 +143,7 @@ mod postgres_tests { cleanup(&db, &votes).await; for (slug, voter_id) in votes.iter() { - insert_new_vote(slug, voter_id, &db) + insert_new_vote(slug, voter_id, None, &db) .await .expect("Insertions to db failed"); } @@ -196,22 +209,22 @@ mod postgres_tests { )]; cleanup(&db, &votes).await; - insert_new_vote(&votes[0].0, &votes[0].1, &db) + insert_new_vote(&votes[0].0, &votes[0].1, None, &db) .await .unwrap(); - insert_new_vote(&votes[0].0, &votes[0].1, &db) + insert_new_vote(&votes[0].0, &votes[0].1, None, &db) .await .unwrap(); - insert_new_vote(&votes[0].0, &votes[0].1, &db) + insert_new_vote(&votes[0].0, &votes[0].1, None, &db) .await .unwrap(); - insert_new_vote(&votes[0].0, &votes[0].1, &db) + insert_new_vote(&votes[0].0, &votes[0].1, None, &db) .await .unwrap(); - insert_new_vote(&votes[0].0, &votes[0].1, &db) + insert_new_vote(&votes[0].0, &votes[0].1, None, &db) .await .unwrap(); - insert_new_vote(&votes[0].0, &votes[0].1, &db) + insert_new_vote(&votes[0].0, &votes[0].1, None, &db) .await .unwrap(); @@ -231,7 +244,7 @@ mod postgres_tests { )]; cleanup(&db, &votes).await; - insert_new_vote(&votes[0].0, &votes[0].1, &db) + insert_new_vote(&votes[0].0, &votes[0].1, None, &db) .await .unwrap(); @@ -265,10 +278,10 @@ mod postgres_tests { ]; cleanup(&db, &votes).await; - insert_new_vote(&votes[0].0, &votes[0].1, &db) + insert_new_vote(&votes[0].0, &votes[0].1, None, &db) .await .unwrap(); - insert_new_vote(&votes[1].0, &votes[1].1, &db) + insert_new_vote(&votes[1].0, &votes[1].1, None, &db) .await .unwrap();