fix(paheko): retry in case of email duplication
This commit is contained in:
parent
978c00c1dc
commit
54bd32d57d
4 changed files with 54 additions and 14 deletions
21
Cargo.lock
generated
21
Cargo.lock
generated
|
@ -292,6 +292,17 @@ dependencies = [
|
||||||
"pin-project-lite",
|
"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]]
|
[[package]]
|
||||||
name = "async-std"
|
name = "async-std"
|
||||||
version = "1.12.0"
|
version = "1.12.0"
|
||||||
|
@ -508,7 +519,7 @@ dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"clap_lex",
|
"clap_lex",
|
||||||
"strsim",
|
"strsim 0.10.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1537,6 +1548,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"argh",
|
"argh",
|
||||||
|
"async-recursion",
|
||||||
"base64_light",
|
"base64_light",
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
|
@ -1550,6 +1562,7 @@ dependencies = [
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"strsim 0.11.0",
|
||||||
"strum 0.25.0",
|
"strum 0.25.0",
|
||||||
"surf",
|
"surf",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
@ -2228,6 +2241,12 @@ version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strum"
|
name = "strum"
|
||||||
version = "0.24.1"
|
version = "0.24.1"
|
||||||
|
|
|
@ -25,3 +25,5 @@ fully_pub = "0.1.4"
|
||||||
base64_light = "0.1.4"
|
base64_light = "0.1.4"
|
||||||
csv = "1.3.0"
|
csv = "1.3.0"
|
||||||
argh = "0.1.12"
|
argh = "0.1.12"
|
||||||
|
strsim = "0.11.0"
|
||||||
|
async-recursion = "1.0.5"
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use async_recursion::async_recursion;
|
||||||
use anyhow::{Context, Result, anyhow};
|
use anyhow::{Context, Result, anyhow};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
@ -53,7 +54,8 @@ struct UserSummary {
|
||||||
id: Id,
|
id: Id,
|
||||||
first_name: Option<String>,
|
first_name: Option<String>,
|
||||||
last_name: String,
|
last_name: String,
|
||||||
email: Option<String>
|
email: Option<String>,
|
||||||
|
phone: Option<String>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
@ -293,7 +295,7 @@ impl AuthentifiedClient {
|
||||||
|
|
||||||
pub async fn get_users(&self) -> Result<Vec<UserSummary>> {
|
pub async fn get_users(&self) -> Result<Vec<UserSummary>> {
|
||||||
let query: String = r#"
|
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();
|
"#.to_string();
|
||||||
|
|
||||||
let users_val = self.sql_query(query).await.context("Fetching users")?;
|
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)
|
pub async fn create_user(&self, user: &GeneralizedAnswer, next_id: u64)
|
||||||
-> Result<UserSummary>
|
-> Result<UserSummary>
|
||||||
{
|
{
|
||||||
|
@ -430,7 +433,16 @@ impl AuthentifiedClient {
|
||||||
.send().await?;
|
.send().await?;
|
||||||
|
|
||||||
if res.status() != 200 {
|
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());
|
return Err(APIClientError::InvalidStatusCode.into());
|
||||||
}
|
}
|
||||||
Ok(
|
Ok(
|
||||||
|
@ -438,7 +450,8 @@ impl AuthentifiedClient {
|
||||||
id: Id(next_id),
|
id: Id(next_id),
|
||||||
first_name: u.first_name,
|
first_name: u.first_name,
|
||||||
last_name: u.last_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) -> () {
|
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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ struct GeneralizedAnswer {
|
||||||
|
|
||||||
fn get_accounting_year_for_time<'a>(accounting_years: &'a Vec<AccountingYear>, time: &'a DateTime<Utc>) -> Option<&'a AccountingYear> {
|
fn get_accounting_year_for_time<'a>(accounting_years: &'a Vec<AccountingYear>, time: &'a DateTime<Utc>) -> Option<&'a AccountingYear> {
|
||||||
let date_ref = time.date_naive().clone();
|
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(
|
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")
|
let mut pk_next_user_service_id = paheko_client.get_next_id("services_users")
|
||||||
.await.context("Get paheko services_users next id")?;
|
.await.context("Get paheko services_users next id")?;
|
||||||
|
|
||||||
for answer_inp in answers {
|
for answer_inp in &answers {
|
||||||
let mut answer = answer_inp;
|
let mut answer = answer_inp.clone();
|
||||||
answer.first_name = answer.first_name.map(normalize_first_name);
|
answer.first_name = answer.first_name.map(normalize_first_name);
|
||||||
answer.last_name = normalize_last_name(answer.last_name);
|
answer.last_name = normalize_last_name(answer.last_name);
|
||||||
|
|
||||||
eprintln!("Processing answer:");
|
eprintln!("Processing answer:");
|
||||||
eprintln!(" email: {:?}", answer.email);
|
|
||||||
eprintln!(" name: {} {}", &answer.last_name, answer.first_name.clone().unwrap_or("".to_string()));
|
eprintln!(" name: {} {}", &answer.last_name, answer.first_name.clone().unwrap_or("".to_string()));
|
||||||
|
eprintln!(" email: {:?}", answer.email);
|
||||||
|
|
||||||
// list of users involved in this answer
|
// list of users involved in this answer
|
||||||
let mut pk_users_summaries: Vec<paheko::UserSummary> = vec![];
|
let mut pk_users_summaries: Vec<paheko::UserSummary> = vec![];
|
||||||
let mut pk_user_service_registrations: Vec<paheko::UserServiceRegistration> = vec![];
|
let mut pk_user_service_registrations: Vec<paheko::UserServiceRegistration> = vec![];
|
||||||
|
|
||||||
// check for existing user in paheko by email
|
// 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
|
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();
|
.cloned();
|
||||||
|
|
||||||
// check for existing transactions
|
// check for existing transactions
|
||||||
|
@ -123,6 +123,7 @@ pub async fn sync_paheko(
|
||||||
// dbg!(&existing_subscriptions);
|
// dbg!(&existing_subscriptions);
|
||||||
let pk_user_summary = match existing_user_opt.clone() {
|
let pk_user_summary = match existing_user_opt.clone() {
|
||||||
Some(user) => {
|
Some(user) => {
|
||||||
|
eprintln!(" Found existing paheko user by name.");
|
||||||
user
|
user
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
|
@ -226,8 +227,13 @@ pub async fn sync_paheko(
|
||||||
|
|
||||||
// add transaction
|
// add transaction
|
||||||
let transaction = paheko::SimpleTransaction {
|
let transaction = paheko::SimpleTransaction {
|
||||||
accounting_year: get_accounting_year_for_time(&accounting_years, &answer.inception_time)
|
accounting_year: match get_accounting_year_for_time(&accounting_years, &answer.inception_time) {
|
||||||
.expect("Cannot find an accounting year that match the date on paheko").id.clone(),
|
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
|
// TODO: make the label template configurable
|
||||||
label: format!("{} {:?} via {}", pk_membership.service_name, pk_membership.mode_name, via_name),
|
label: format!("{} {:?} via {}", pk_membership.service_name, pk_membership.mode_name, via_name),
|
||||||
amount: pk_membership.payed_amount,
|
amount: pk_membership.payed_amount,
|
||||||
|
|
Loading…
Reference in a new issue