Finalized member migration

This commit is contained in:
xeovalyte 2025-01-31 14:37:48 +01:00
parent 9cf9e5752f
commit 26f5195069
3 changed files with 65 additions and 12 deletions

View File

@ -1,5 +1,5 @@
use rand::distr::{Alphanumeric, SampleString}; use rand::distr::{Alphanumeric, SampleString};
use sqlx::{Postgres, QueryBuilder}; use sqlx::{PgPool, Postgres, QueryBuilder};
use validator::Validate; use validator::Validate;
#[derive(Debug, Validate)] #[derive(Debug, Validate)]
@ -15,10 +15,26 @@ pub struct Member {
} }
impl Member { impl Member {
pub async fn insert_multiple( pub async fn get_many(transaction: &PgPool, members: Vec<Self>) -> Result<(), sqlx::Error> {
Ok(())
}
pub async fn get_all(pool: &PgPool) -> Result<Vec<Self>, 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>, transaction: &mut sqlx::Transaction<'_, Postgres>,
members: Vec<Self>, members: Vec<Self>,
) -> Result<(), sqlx::Error> { ) -> Result<(), sqlx::Error> {
if members.len() == 0 {
return Ok(());
}
let mut query_builder = QueryBuilder::new( let mut query_builder = QueryBuilder::new(
"INSERT INTO members(id, first_name, full_name, registration_token, diploma, hours, groups) " "INSERT INTO members(id, first_name, full_name, registration_token, diploma, hours, groups) "
); );
@ -41,14 +57,34 @@ impl Member {
Ok(()) Ok(())
} }
pub async fn update_multiple( pub async fn update_many(
transaction: &mut sqlx::Transaction<'_, Postgres>, transaction: &mut sqlx::Transaction<'_, Postgres>,
members: Vec<Self>, members: Vec<Self>,
) -> Result<(), sqlx::Error> { ) -> Result<(), sqlx::Error> {
if members.len() == 0 {
return Ok(());
}
for member in members { 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?; 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(()) Ok(())
} }
pub async fn remove_many(
transaction: &mut sqlx::Transaction<'_, Postgres>,
member_ids: &Vec<String>,
) -> Result<(), sqlx::Error> {
sqlx::query!(
"
DELETE FROM members WHERE id = ANY($1)
",
member_ids
)
.execute(&mut **transaction)
.await?;
Ok(())
}
} }

View File

@ -1,10 +1,10 @@
#[derive(Clone, serde::Serialize)] #[derive(Debug, Clone, serde::Serialize)]
pub struct Name { pub struct Name {
pub first: String, pub first: String,
pub full: String, pub full: String,
} }
#[derive(Clone, serde::Serialize)] #[derive(Debug, Clone, serde::Serialize)]
pub struct Member { pub struct Member {
pub id: String, pub id: String,
pub name: Name, pub name: Name,

View File

@ -21,14 +21,15 @@ pub async fn migrate_request<'a>(
return Err(AuthError::NoPermssions.into()); return Err(AuthError::NoPermssions.into());
} }
tracing::info!("Migration is requested");
// Convert the input CSV to a vector of members // Convert the input CSV to a vector of members
let members_new: Vec<Member> = Row::from_csv_multiple(&body)? let members_new: Vec<Member> = Row::from_csv_many(&body)?
.into_iter() .into_iter()
.map(|m| m.into()) .map(|m| m.into())
.collect(); .collect();
// TODO: Write function to get members from database let members_old = convert_vec(DbMember::get_all(&state.pool).await?);
let members_old: Vec<Member> = Vec::new();
let members_diff = generate_diff(members_new, members_old); let members_diff = generate_diff(members_new, members_old);
@ -50,6 +51,8 @@ pub async fn migrate_confirm<'a>(
return Err(AuthError::NoPermssions.into()); return Err(AuthError::NoPermssions.into());
} }
tracing::info!("Migration is confirmed");
// TODO: Implement better error naming // TODO: Implement better error naming
let count = match body.trim().parse::<u32>() { let count = match body.trim().parse::<u32>() {
Ok(c) => c, Ok(c) => c,
@ -63,16 +66,30 @@ pub async fn migrate_confirm<'a>(
None => return Err(crate::Error::NotFound), 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?; migrate_transaction(&state.pool, members_diff).await?;
tracing::info!(
"Migration is successfully executed. Inserted: {}, updated: {}, removed: {}",
inserted_len,
update_len,
remove_len
);
Ok(()) Ok(())
} }
async fn migrate_transaction(pool: &PgPool, members_diff: MembersDiff) -> Result<(), sqlx::Error> { async fn migrate_transaction(pool: &PgPool, members_diff: MembersDiff) -> Result<(), sqlx::Error> {
let mut transaction = pool.begin().await?; let mut transaction = pool.begin().await?;
// DbMember::insert_multiple(&mut transaction, convert_vec(members_diff.insert)).await?; DbMember::insert_many(&mut transaction, convert_vec(members_diff.insert)).await?;
DbMember::update_multiple(&mut transaction, convert_vec(members_diff.update)).await?; DbMember::update_many(&mut transaction, convert_vec(members_diff.update)).await?;
let members_remove_ids: Vec<String> = members_diff.remove.into_iter().map(|m| m.id).collect();
DbMember::remove_many(&mut transaction, &members_remove_ids).await?;
transaction.commit().await?; transaction.commit().await?;
@ -98,7 +115,7 @@ struct Row {
diploma: Option<String>, diploma: Option<String>,
} }
#[derive(Clone)] #[derive(Debug, Clone)]
pub struct MembersDiff { pub struct MembersDiff {
insert: Vec<Member>, insert: Vec<Member>,
update: Vec<Member>, update: Vec<Member>,
@ -128,7 +145,7 @@ impl Default for MigrationStore {
} }
impl Row { impl Row {
fn from_csv_multiple(input: &str) -> Result<Vec<Self>, csv::Error> { fn from_csv_many(input: &str) -> Result<Vec<Self>, csv::Error> {
let mut rdr = csv::ReaderBuilder::new() let mut rdr = csv::ReaderBuilder::new()
.delimiter(b';') .delimiter(b';')
.from_reader(input.as_bytes()); .from_reader(input.as_bytes());