From 54bd32d57da85e868a8f3f9b3452427d4274400d Mon Sep 17 00:00:00 2001 From: Matthieu Bessat Date: Fri, 19 Jan 2024 15:58:07 +0100 Subject: [PATCH] fix(paheko): retry in case of email duplication --- Cargo.lock | 21 ++++++++++++++++++++- Cargo.toml | 2 ++ src/paheko.rs | 23 ++++++++++++++++++----- src/sync_paheko.rs | 22 ++++++++++++++-------- 4 files changed, 54 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ea3b0ea..bf93176 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -292,6 +292,17 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-recursion" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.43", +] + [[package]] name = "async-std" version = "1.12.0" @@ -508,7 +519,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim", + "strsim 0.10.0", ] [[package]] @@ -1537,6 +1548,7 @@ version = "0.1.0" dependencies = [ "anyhow", "argh", + "async-recursion", "base64_light", "chrono", "clap", @@ -1550,6 +1562,7 @@ dependencies = [ "reqwest", "serde", "serde_json", + "strsim 0.11.0", "strum 0.25.0", "surf", "thiserror", @@ -2228,6 +2241,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strsim" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" + [[package]] name = "strum" version = "0.24.1" diff --git a/Cargo.toml b/Cargo.toml index 36d865a..f9e6598 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,3 +25,5 @@ fully_pub = "0.1.4" base64_light = "0.1.4" csv = "1.3.0" argh = "0.1.12" +strsim = "0.11.0" +async-recursion = "1.0.5" diff --git a/src/paheko.rs b/src/paheko.rs index 230614a..cd10ccf 100644 --- a/src/paheko.rs +++ b/src/paheko.rs @@ -1,3 +1,4 @@ +use async_recursion::async_recursion; use anyhow::{Context, Result, anyhow}; use url::Url; use serde::{Serialize, Deserialize}; @@ -53,7 +54,8 @@ struct UserSummary { id: Id, first_name: Option, last_name: String, - email: Option + email: Option, + phone: Option } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -293,7 +295,7 @@ impl AuthentifiedClient { pub async fn get_users(&self) -> Result> { let query: String = r#" - SELECT id,nom AS first_name,last_name,email FROM users; + SELECT id,nom AS first_name,last_name,email,telephone AS phone FROM users; "#.to_string(); let users_val = self.sql_query(query).await.context("Fetching users")?; @@ -389,6 +391,7 @@ impl AuthentifiedClient { ) } + #[async_recursion] pub async fn create_user(&self, user: &GeneralizedAnswer, next_id: u64) -> Result { @@ -430,7 +433,16 @@ impl AuthentifiedClient { .send().await?; if res.status() != 200 { - self.show_paheko_err(res).await; + let res_text = res.text().await.unwrap(); + if res_text.contains("E-Mail") && res_text.contains("unique") { + eprintln!("WARN: Detected duplicated email, will retry without email"); + // email detected as duplicated by paheko server + let mut new_data = user.clone(); + new_data.email = None; + return self.create_user(&new_data, next_id).await; + } + + // self.show_paheko_err(res).await; return Err(APIClientError::InvalidStatusCode.into()); } Ok( @@ -438,7 +450,8 @@ impl AuthentifiedClient { id: Id(next_id), first_name: u.first_name, last_name: u.last_name, - email: u.email + email: u.email, + phone: u.phone } ) } @@ -525,6 +538,6 @@ impl AuthentifiedClient { } async fn show_paheko_err(&self, err_response: reqwest::Response) -> () { - eprintln!("Paheko Error Details: {:?} {:?}", err_response.status(), err_response.text().await.unwrap()) + eprintln!("Paheko error details: {:?} {:?}", err_response.status(), err_response.text().await.unwrap()) } } diff --git a/src/sync_paheko.rs b/src/sync_paheko.rs index 33945cc..93a944b 100644 --- a/src/sync_paheko.rs +++ b/src/sync_paheko.rs @@ -51,7 +51,7 @@ struct GeneralizedAnswer { fn get_accounting_year_for_time<'a>(accounting_years: &'a Vec, time: &'a DateTime) -> Option<&'a AccountingYear> { let date_ref = time.date_naive().clone(); - accounting_years.iter().find(|year| year.start_date < date_ref && date_ref < year.end_date) + accounting_years.iter().find(|year| year.start_date <= date_ref && date_ref <= year.end_date) } pub async fn sync_paheko( @@ -93,23 +93,23 @@ pub async fn sync_paheko( let mut pk_next_user_service_id = paheko_client.get_next_id("services_users") .await.context("Get paheko services_users next id")?; - for answer_inp in answers { - let mut answer = answer_inp; + for answer_inp in &answers { + let mut answer = answer_inp.clone(); answer.first_name = answer.first_name.map(normalize_first_name); answer.last_name = normalize_last_name(answer.last_name); eprintln!("Processing answer:"); - eprintln!(" email: {:?}", answer.email); eprintln!(" name: {} {}", &answer.last_name, answer.first_name.clone().unwrap_or("".to_string())); + eprintln!(" email: {:?}", answer.email); // list of users involved in this answer let mut pk_users_summaries: Vec = vec![]; let mut pk_user_service_registrations: Vec = vec![]; // check for existing user in paheko by email - // TODO: check user with fuzzing + // TODO: check user with fuzzing first name and last name let existing_user_opt = existing_users - .iter().find(|user| user.email == answer.email) + .iter().find(|user| user.first_name == answer.first_name && user.last_name == answer.last_name) .cloned(); // check for existing transactions @@ -123,6 +123,7 @@ pub async fn sync_paheko( // dbg!(&existing_subscriptions); let pk_user_summary = match existing_user_opt.clone() { Some(user) => { + eprintln!(" Found existing paheko user by name."); user }, None => { @@ -226,8 +227,13 @@ pub async fn sync_paheko( // add transaction let transaction = paheko::SimpleTransaction { - accounting_year: get_accounting_year_for_time(&accounting_years, &answer.inception_time) - .expect("Cannot find an accounting year that match the date on paheko").id.clone(), + accounting_year: match get_accounting_year_for_time(&accounting_years, &answer.inception_time) { + None => { + eprintln!("Cannot find an accounting year on paheko that include the inception date {:?} given", &answer.inception_time); + panic!(); + }, + Some(s) => s + }.id.clone(), // TODO: make the label template configurable label: format!("{} {:?} via {}", pk_membership.service_name, pk_membership.mode_name, via_name), amount: pk_membership.payed_amount,