From 26f51950699dc2bca296dd04df0641b9131f4ee0 Mon Sep 17 00:00:00 2001 From: xeovalyte Date: Fri, 31 Jan 2025 14:37:48 +0100 Subject: [PATCH] Finalized member migration --- server/src/database/model/member.rs | 42 ++++++++++++++++++++++++++--- server/src/model/member.rs | 4 +-- server/src/routes/member/migrate.rs | 31 ++++++++++++++++----- 3 files changed, 65 insertions(+), 12 deletions(-) diff --git a/server/src/database/model/member.rs b/server/src/database/model/member.rs index 644138f..5e4b74c 100644 --- a/server/src/database/model/member.rs +++ b/server/src/database/model/member.rs @@ -1,5 +1,5 @@ use rand::distr::{Alphanumeric, SampleString}; -use sqlx::{Postgres, QueryBuilder}; +use sqlx::{PgPool, Postgres, QueryBuilder}; use validator::Validate; #[derive(Debug, Validate)] @@ -15,10 +15,26 @@ pub struct Member { } impl Member { - pub async fn insert_multiple( + pub async fn get_many(transaction: &PgPool, members: Vec) -> Result<(), sqlx::Error> { + Ok(()) + } + + pub async fn get_all(pool: &PgPool) -> Result, sqlx::Error> { + let members = sqlx::query_as!(Member, "SELECT * FROM members;",) + .fetch_all(pool) + .await?; + + Ok(members) + } + + pub async fn insert_many( transaction: &mut sqlx::Transaction<'_, Postgres>, members: Vec, ) -> Result<(), sqlx::Error> { + if members.len() == 0 { + return Ok(()); + } + let mut query_builder = QueryBuilder::new( "INSERT INTO members(id, first_name, full_name, registration_token, diploma, hours, groups) " ); @@ -41,14 +57,34 @@ impl Member { Ok(()) } - pub async fn update_multiple( + pub async fn update_many( transaction: &mut sqlx::Transaction<'_, Postgres>, members: Vec, ) -> Result<(), sqlx::Error> { + if members.len() == 0 { + return Ok(()); + } + for member in members { sqlx::query!("UPDATE ONLY members SET first_name = $1, full_name = $2, diploma = $3, hours = $4, groups = $5 WHERE id = $6", member.first_name, member.full_name, member.diploma, &member.hours, &member.groups, member.id).execute(&mut **transaction).await?; } Ok(()) } + + pub async fn remove_many( + transaction: &mut sqlx::Transaction<'_, Postgres>, + member_ids: &Vec, + ) -> Result<(), sqlx::Error> { + sqlx::query!( + " + DELETE FROM members WHERE id = ANY($1) + ", + member_ids + ) + .execute(&mut **transaction) + .await?; + + Ok(()) + } } diff --git a/server/src/model/member.rs b/server/src/model/member.rs index 1fd43d2..1d5d274 100644 --- a/server/src/model/member.rs +++ b/server/src/model/member.rs @@ -1,10 +1,10 @@ -#[derive(Clone, serde::Serialize)] +#[derive(Debug, Clone, serde::Serialize)] pub struct Name { pub first: String, pub full: String, } -#[derive(Clone, serde::Serialize)] +#[derive(Debug, Clone, serde::Serialize)] pub struct Member { pub id: String, pub name: Name, diff --git a/server/src/routes/member/migrate.rs b/server/src/routes/member/migrate.rs index aa75db7..a2a3b52 100644 --- a/server/src/routes/member/migrate.rs +++ b/server/src/routes/member/migrate.rs @@ -21,14 +21,15 @@ pub async fn migrate_request<'a>( return Err(AuthError::NoPermssions.into()); } + tracing::info!("Migration is requested"); + // Convert the input CSV to a vector of members - let members_new: Vec = Row::from_csv_multiple(&body)? + let members_new: Vec = Row::from_csv_many(&body)? .into_iter() .map(|m| m.into()) .collect(); - // TODO: Write function to get members from database - let members_old: Vec = Vec::new(); + let members_old = convert_vec(DbMember::get_all(&state.pool).await?); let members_diff = generate_diff(members_new, members_old); @@ -50,6 +51,8 @@ pub async fn migrate_confirm<'a>( return Err(AuthError::NoPermssions.into()); } + tracing::info!("Migration is confirmed"); + // TODO: Implement better error naming let count = match body.trim().parse::() { Ok(c) => c, @@ -63,16 +66,30 @@ pub async fn migrate_confirm<'a>( None => return Err(crate::Error::NotFound), }; + let inserted_len = members_diff.insert.len(); + let update_len = members_diff.update.len(); + let remove_len = members_diff.remove.len(); + migrate_transaction(&state.pool, members_diff).await?; + tracing::info!( + "Migration is successfully executed. Inserted: {}, updated: {}, removed: {}", + inserted_len, + update_len, + remove_len + ); + Ok(()) } async fn migrate_transaction(pool: &PgPool, members_diff: MembersDiff) -> Result<(), sqlx::Error> { let mut transaction = pool.begin().await?; - // DbMember::insert_multiple(&mut transaction, convert_vec(members_diff.insert)).await?; - DbMember::update_multiple(&mut transaction, convert_vec(members_diff.update)).await?; + DbMember::insert_many(&mut transaction, convert_vec(members_diff.insert)).await?; + DbMember::update_many(&mut transaction, convert_vec(members_diff.update)).await?; + + let members_remove_ids: Vec = members_diff.remove.into_iter().map(|m| m.id).collect(); + DbMember::remove_many(&mut transaction, &members_remove_ids).await?; transaction.commit().await?; @@ -98,7 +115,7 @@ struct Row { diploma: Option, } -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct MembersDiff { insert: Vec, update: Vec, @@ -128,7 +145,7 @@ impl Default for MigrationStore { } impl Row { - fn from_csv_multiple(input: &str) -> Result, csv::Error> { + fn from_csv_many(input: &str) -> Result, csv::Error> { let mut rdr = csv::ReaderBuilder::new() .delimiter(b';') .from_reader(input.as_bytes());