diff --git a/src/api/camp_form.rs b/src/api/camp_form.rs index fc82b66..5efb9ac 100644 --- a/src/api/camp_form.rs +++ b/src/api/camp_form.rs @@ -2,271 +2,219 @@ use std::collections::HashMap; use actix_multipart::form::{text::Text, MultipartForm}; use actix_web::{http::StatusCode, post, HttpResponse, HttpResponseBuilder}; -use lettre::{message::MultiPart, Message}; -use maud::{html, DOCTYPE}; -use reqwest::{Client, Error}; -use tracing::info; +use color_eyre::eyre::{Context, Result}; +use futures::FutureExt; +use lettre::{message::SinglePart, Message}; +use maud::{html, Markup, DOCTYPE}; +use reqwest::Client; +use tracing::{error, info}; -use crate::email::send_email; - -#[derive(Debug, MultipartForm, Default)] +#[derive(Debug, MultipartForm)] struct CampForm { #[multipart(rename = "first-name")] - first_name: Option>, + first_name: Text, #[multipart(rename = "last-name")] - last_name: Option>, + last_name: Text, #[multipart(rename = "parent-first-name")] - parent_first_name: Option>, + parent_first_name: Text, #[multipart(rename = "parent-last-name")] - parent_last_name: Option>, + parent_last_name: Text, #[multipart(rename = "birth-date")] - birthdate: Option>, - gender: Option>, - street: Option>, - city: Option>, - state: Option>, - zip: Option>, + birthdate: Text, + gender: Text, + street: Text, + city: Text, + state: Text, + zip: Text, #[multipart(rename = "parent-phone")] - parent_phone: Option>, + parent_phone: Text, #[multipart(rename = "parent-email")] - parent_email: Option>, - grade: Option>, - shirt: Option>, - allergies: Option>, - week: Option>, - registration: Option>, + parent_email: Text, + grade: Text, + shirt: Text, + allergies: Text, + week: Text, + registration: Text, #[multipart(rename = "health-form")] - health_form: Option>, + health_form: Text, +} + +impl From<&CampForm> for HashMap { + fn from(form: &CampForm) -> Self { + let mut map = HashMap::new(); + map.insert(63, format!("{} {}", form.first_name.0, form.last_name.0)); + map.insert( + 64, + format!("{} {}", form.parent_first_name.0, form.parent_last_name.0), + ); + map.insert(65, form.parent_phone.0.clone()); + map.insert(66, form.parent_email.0.clone().clone()); + map.insert(67, form.birthdate.0.clone()); + map.insert(69, form.gender.0.clone()); + map.insert(70, form.street.0.clone()); + map.insert(71, form.city.0.clone()); + map.insert(72, form.state.0.clone()); + map.insert(73, form.zip.0.clone().to_string()); + map.insert(74, form.grade.0.clone()); + map.insert(75, form.week.0.clone()); + map.insert(76, form.shirt.0.clone()); + map.insert(77, form.registration.0.clone()); + map.insert(115, form.health_form.0.clone()); + map + } +} + +impl CampForm { + fn build_email(&self) -> Markup { + html! { + (DOCTYPE) + meta charset="utf-8"; + html { + head { + title { (self.first_name.0) " " (self.last_name.0) " signed up for camp!" } + style { + "table { border-collapse: collapse; width: 100% }" + "td, th { padding: 8px }" + "td { text-align: left; width: 70%; word-wrap: break-word }" + "th { text-align: right; border-right: 1px solid #ddd }" + "tr { border-bottom: 1px solid #ddd }" + "h1 { text-align: center }" + } + } + body { + h1 { "Camp form for " (self.first_name.0) " " (self.last_name.0) "!" } + hr; + table { + tr { + th { "Name" } + td { (self.first_name.0) " " (self.last_name.0) } + } + tr { + th { "Parent" } + td { (self.parent_first_name.0) " " (self.parent_last_name.0) } + } + tr { + th { "Birthdate" } + td { (self.birthdate.0) } + } + tr { + th { "Gender" } + td { (self.gender.0) } + } + tr { + th { "Street" } + td { (self.street.0) } + } + tr { + th { "City" } + td { (self.city.0) } + } + tr { + th { "State" } + td { (self.state.0) } + } + tr { + th { "Zip" } + td { (self.zip.0) } + } + tr { + th { "Parent Phone" } + td { (self.parent_phone.0) } + } + tr { + th { "Parent Email" } + td { (self.parent_email.0) } + } + tr { + th { "Grade" } + td { (self.grade.0) } + } + tr { + th { "Camper Allergies" } + td { (self.allergies.0) } + } + tr { + th { "T-Shirt Size" } + td { (self.shirt.0) } + } + tr { + th { "Week Choice" } + td { (self.week.0) } + } + tr { + th { "Health Form" } + td { (self.health_form.0) } + } + tr { + th { "Registration" } + td { (self.registration.0) } + } + } + } + } + } + } + + fn prepare_email(&self) -> Result { + let first = self.first_name.clone(); + let last = self.last_name.clone(); + let email_subject = format!("{} {} signed up for camp!", first, last); + info!("{first} {last} signed up for camp!"); + let email = self.build_email(); + // let temp_file = self.get_temp_file(); + // let multi = if let Some((file, path, content_type)) = temp_file { + // let filebody = fs::read(path); + // let content_type = + // ContentType::parse(&content_type.unwrap_or(String::from("image/jpg"))).unwrap(); + // let attachment = Attachment::new(file).body(filebody.unwrap(), content_type); + // // info!(?attachment); + // MultiPart::mixed() + // .singlepart(SinglePart::html(email.into_string())) + // .singlepart(attachment) + // } else { + // MultiPart::alternative_plain_html(String::from("Testing"), email.into_string()) + // }; + + let singlepart = SinglePart::html(email.into_string()); + + Message::builder() + .from( + "TFC ADMIN " + .parse() + .unwrap(), + ) + .to("Chris Cochrun ".parse().unwrap()) + .to("Ethan Rose ".parse().unwrap()) + .subject(email_subject) + .singlepart(singlepart) + // .multipart(multi) + .wrap_err("problemss") + } } #[post("/camp-form")] pub async fn camp_form(MultipartForm(form): MultipartForm) -> HttpResponse { - info!("a new form"); - let first = form - .first_name - .as_ref() - .unwrap_or(&Text(String::from(""))) - .0 - .clone(); - let last = form - .last_name - .as_ref() - .unwrap_or(&Text(String::from(""))) - .0 - .clone(); - let full_name = format!("{} {}", first, last); - let email_subject = format!("{} {} signed up for camp!", first, last); - let parent = format!( - "{} {}", - form.parent_first_name - .as_ref() - .unwrap_or(&Text(String::from(""))) - .0 - .clone(), - form.parent_last_name - .as_ref() - .unwrap_or(&Text(String::from(""))) - .0 - .clone() - ); - let birthdate = form - .birthdate - .as_ref() - .unwrap_or(&Text(String::from(""))) - .0 - .clone(); - let gender = form - .gender - .as_ref() - .unwrap_or(&Text(String::from(""))) - .0 - .clone(); - let street = form - .street - .as_ref() - .unwrap_or(&Text(String::from(""))) - .0 - .clone(); - let city = form - .city - .as_ref() - .unwrap_or(&Text(String::from(""))) - .0 - .clone(); - let state = form - .state - .as_ref() - .unwrap_or(&Text(String::from(""))) - .0 - .clone(); - let zip = form.zip.as_ref().unwrap_or(&Text(0)).0; - let parent_phone = form - .parent_phone - .as_ref() - .unwrap_or(&Text(String::from(""))) - .0 - .clone(); - let parent_email = form - .parent_email - .as_ref() - .unwrap_or(&Text(String::from(""))) - .0 - .clone(); - let grade = form - .grade - .as_ref() - .unwrap_or(&Text(String::from(""))) - .0 - .clone(); - let shirt = form - .shirt - .as_ref() - .unwrap_or(&Text(String::from(""))) - .0 - .clone(); - let allergies = form - .allergies - .as_ref() - .unwrap_or(&Text(String::from(""))) - .0 - .clone(); - let week = form - .week - .as_ref() - .unwrap_or(&Text(String::from(""))) - .0 - .clone(); - let registration = form - .registration - .as_ref() - .unwrap_or(&Text(String::from(""))) - .0 - .clone(); - let health = form - .health_form - .as_ref() - .unwrap_or(&Text(String::from(""))) - .0 - .clone(); - let reg = registration.clone(); - - info!("Sending post to database"); - - info!("{first} {last} signed up for camp!"); - let email = html! { - (DOCTYPE) - meta charset="utf-8"; - html { - head { - title { (first) " " (last) " signed up for camp!" } - style { - "table { border-collapse: collapse; width: 100% }" - "td, th { padding: 8px }" - "td { text-align: left; width: 70%; word-wrap: break-word }" - "th { text-align: right; border-right: 1px solid #ddd }" - "tr { border-bottom: 1px solid #ddd }" - "h1 { text-align: center }" - } - } - body { - h1 { "Camp form for " (first) " " (last) } - hr; - table { - tr { - th { "Name" } - td { (first) " " (last) } - } - tr { - th { "Parent" } - td { (parent) } - } - tr { - th { "Birthdate" } - td { (birthdate) } - } - tr { - th { "Gender" } - td { (gender) } - } - tr { - th { "Street" } - td { (street) } - } - tr { - th { "City" } - td { (city) } - } - tr { - th { "State" } - td { (state) } - } - tr { - th { "Zip" } - td { (zip) } - } - tr { - th { "Parent Phone" } - td { (parent_phone) } - } - tr { - th { "Parent Email" } - td { (parent_email) } - } - tr { - th { "Grade" } - td { (grade) } - } - tr { - th { "Camper Allergies" } - td { (allergies) } - } - tr { - th { "T-Shirt Size" } - td { (shirt) } - } - tr { - th { "Week Choice" } - td { (week) } - } - tr { - th { "Health Form" } - td { (health) } - } - tr { - th { "Registration" } - td { (registration) } - } - } - } - } - }; - let multi = MultiPart::alternative_plain_html( - String::from("A camp form was filled out!"), - email.into_string(), - ); - - if let Ok(m) = Message::builder() - .from( - "TFC ADMIN " - .parse() - .unwrap(), - ) - .to("Chris Cochrun ".parse().unwrap()) - .to("Ethan Rose ".parse().unwrap()) - .subject(email_subject) - .multipart(multi) - { - let _ = send_email(m); - } else { - info!("Email incorrect"); - } - - match store_camp_form(form).await { + let full_name = format!("{} {}", form.first_name.0, form.last_name.0); + let map = (&form).into(); + let future = store_camp_form(map); + actix_rt::spawn(future.map(|s| match s { Ok(_) => info!("Successfully posted to nextcloud tables"), Err(e) => log::error!("Error in posting camp data: {:?}", e), - } + })); - match health.as_str() { + let email = form.prepare_email(); + match email { + Ok(m) => { + let sent = crate::email::send_email(m); + actix_rt::spawn(sent.map(|s| match s { + Ok(_) => info!("Successfully sent form to email!"), + Err(e) => error!("There was an erroring sending form to email: {e}"), + })); + } + Err(e) => error!("error sending email {e}"), + }; + + match form.health_form.0.as_str() { "now" => { info!("Sending them to fill out the health form"); HttpResponse::Ok() @@ -275,12 +223,12 @@ pub async fn camp_form(MultipartForm(form): MultipartForm) -> HttpResp "HX-Redirect", format!( "https://tfcconnection.org/camp-health-form/?registration={}", - reg.as_str() + form.registration.0.as_str() ), )) .finish() } - "later" => match reg.as_str() { + "later" => match form.registration.0.as_str() { "now" => { info!("Sending them to pay for registration now"); HttpResponse::Ok() @@ -343,46 +291,70 @@ pub async fn camp_form(MultipartForm(form): MultipartForm) -> HttpResp } } -async fn store_camp_form(form: CampForm) -> Result<(), Error> { +async fn store_camp_form(map: HashMap) -> Result<()> { let request = Client::new(); - let mut map = HashMap::new(); - map.insert( - 63, - format!( - "{} {}", - &form.first_name.unwrap_or(Text(String::new())).0, - &form.last_name.unwrap_or(Text(String::new())).0 - ), - ); - map.insert( - 64, - format!( - "{} {}", - form.parent_first_name.unwrap_or(Text(String::new())).0, - form.parent_last_name.unwrap_or(Text(String::new())).0 - ), - ); - map.insert(65, form.parent_phone.unwrap_or(Text(String::new())).0); - map.insert(66, form.parent_email.unwrap_or(Text(String::new())).0); - map.insert(67, form.birthdate.unwrap_or(Text(String::new())).0); - map.insert(69, form.gender.unwrap_or(Text(String::new())).0); - map.insert(70, form.street.unwrap_or(Text(String::new())).0); - map.insert(71, form.city.unwrap_or(Text(String::new())).0); - map.insert(72, form.state.unwrap_or(Text(String::new())).0); - map.insert(73, form.zip.unwrap_or(Text(0)).0.to_string()); - map.insert(74, form.grade.unwrap_or(Text(String::new())).0); - map.insert(75, form.week.unwrap_or(Text(String::new())).0); - map.insert(76, form.shirt.unwrap_or(Text(String::new())).0); - map.insert(77, form.registration.unwrap_or(Text(String::new())).0); - map.insert(115, form.health_form.unwrap_or(Text(String::new())).0); let mut json = HashMap::new(); json.insert("data", map); request .post("https://staff.tfcconnection.org/apps/tables/api/1/tables/5/rows") - // .header("xc-token", "Ohah24HNGXVvvixv8tvVJW5uNNdWjDJHG1d4t3o9") .basic_auth("chris", Some("2VHeGxeC^Zf9KqFK^G@Pt!zu2q^6@b")) + .header("OCS-APIRequest", "true") + .header("Content-Type", "application/json") .json(&json) .send() .await?; Ok(()) } + +#[cfg(test)] +mod test { + use super::*; + use actix_web::test; + + fn form() -> CampForm { + CampForm { + first_name: Text("Frodo".into()), + last_name: Text("Braggins".into()), + parent_first_name: Text("Bilbo".into()), + parent_last_name: Text("Braggins".into()), + birthdate: Text(String::from("1845-09-12")), + gender: Text(String::from("male")), + street: Text(String::from("1234 Bag End")), + city: Text(String::from("The Shire")), + state: Text(String::from("Hobbiton")), + zip: Text(88888), + parent_phone: Text(String::from("1234567898")), + parent_email: Text(String::from("bilbo@hobbits.com")), + grade: Text(String::from("junior")), + shirt: Text(String::from("medium")), + allergies: Text(String::from("Cool beans")), + week: Text(String::from("1")), + registration: Text(String::from("later")), + health_form: Text(String::from("I guess")), + } + } + + #[test] + async fn test_nc_post() { + let form = form(); + assert!(!form.first_name.is_empty()); + let map = HashMap::from(&form); + let res = store_camp_form(map).await; + match res { + Ok(_) => assert!(true), + Err(e) => assert!(false, "Failed storing test: {e}"), + } + } + + #[test] + async fn test_email() { + let form = form(); + assert!(!form.first_name.is_empty()); + match form.prepare_email() { + Ok(m) => { + assert!(crate::email::send_email(m).await.is_ok()) + } + Err(e) => assert!(false, "Failed emailing test: {e}"), + } + } +} diff --git a/src/api/mt_form.rs b/src/api/mt_form.rs index 6d88c84..1e0752a 100644 --- a/src/api/mt_form.rs +++ b/src/api/mt_form.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, fs, io::Write}; +use std::{collections::HashMap, fs}; use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm}; use actix_web::{post, HttpResponse}; @@ -269,32 +269,6 @@ impl MtForm { } } - async fn store_form(&self) -> Result<()> { - let client = Client::new(); - let map = HashMap::from(self); - let mut json = HashMap::new(); - json.insert("data", map); - - let link = r#"https://staff.tfcconnection.org/apps/tables/#/table/9/row/757"#; - let res = client - .post("https://staff.tfcconnection.org/ocs/v2.php/apps/tables/api/2/tables/9/rows") - .basic_auth("chris", Some("2VHeGxeC^Zf9KqFK^G@Pt!zu2q^6@b")) - .header("OCS-APIRequest", "true") - .header("Content-Type", "application/json") - .json(&json) - .send() - .await?; - if res.status().is_success() { - let res = res.text().await.unwrap(); - Ok(()) - } else { - Err(eyre!( - "Problem in storing data: {:?}", - res.error_for_status() - )) - } - } - fn get_temp_file(&self) -> Option<(String, String, Option)> { let first = self.first_name.clone(); let last = self.last_name.clone(); @@ -339,7 +313,7 @@ impl MtForm { } } - fn send_email(&self) -> Result { + fn prepare_email(&self) -> Result { let first = self.first_name.clone(); let last = self.last_name.clone(); let email_subject = format!("{} {} signed up for mission trip!", first, last); @@ -382,7 +356,7 @@ pub async fn mt_form(MultipartForm(form): MultipartForm) -> HttpResponse Ok(_) => info!("Successfully sent form to nextcloud!"), Err(e) => error!("There was an erroring sending form to nextcloud: {e}"), })); - let email = form.send_email(); + let email = form.prepare_email(); match email { Ok(m) => { let sent = crate::email::send_email(m); @@ -453,7 +427,6 @@ async fn store_form(map: HashMap) -> Result<()> { let mut json = HashMap::new(); json.insert("data", map); - let link = r#"https://staff.tfcconnection.org/apps/tables/#/table/9/row/757"#; let res = client .post("https://staff.tfcconnection.org/ocs/v2.php/apps/tables/api/2/tables/9/rows") .basic_auth("chris", Some("2VHeGxeC^Zf9KqFK^G@Pt!zu2q^6@b")) @@ -463,7 +436,7 @@ async fn store_form(map: HashMap) -> Result<()> { .send() .await?; if res.status().is_success() { - let res = res.text().await.unwrap(); + // let res = res.text().await.unwrap(); Ok(()) } else { Err(eyre!( @@ -476,9 +449,6 @@ async fn store_form(map: HashMap) -> Result<()> { #[cfg(test)] mod test { use actix_web::test; - use pretty_assertions::assert_eq; - use sqlx::Connection; - use tracing::debug; use super::*; @@ -527,7 +497,8 @@ mod test { async fn test_nc_post() { let form = form(); assert!(!form.first_name.is_empty()); - let res = form.store_form().await; + let map = HashMap::from(&form); + let res = store_form(map).await; match res { Ok(_) => assert!(true, "passed storing test"), Err(e) => assert!(false, "Failed storing test: {e}"), @@ -536,10 +507,12 @@ mod test { #[test] async fn test_email() { - let mut form = form(); + let form = form(); assert!(!form.first_name.is_empty()); - match form.send_email() { - Ok(_) => assert!(true, "passed emailing test"), + match form.prepare_email() { + Ok(m) => { + assert!(crate::email::send_email(m).await.is_ok()) + } Err(e) => assert!(false, "Failed emailing test: {e}"), } } diff --git a/src/api/mt_parent_form.rs b/src/api/mt_parent_form.rs index 38137eb..b940e40 100644 --- a/src/api/mt_parent_form.rs +++ b/src/api/mt_parent_form.rs @@ -153,7 +153,7 @@ impl MtParentForm { } } - async fn send_email(&mut self) -> Result<()> { + async fn send_email(&self) -> Result<()> { let first = self.student_first_name.clone(); let last = self.student_last_name.clone(); let email_subject = format!("Parent reference form for {} {}!", first, last); @@ -180,7 +180,7 @@ impl MtParentForm { } #[post("/api/mt-parent-form")] -pub async fn mt_parent_form(MultipartForm(mut form): MultipartForm) -> HttpResponse { +pub async fn mt_parent_form(MultipartForm(form): MultipartForm) -> HttpResponse { match form.store_form().await { Ok(_) => info!("Successfully sent form to nextcloud!"), Err(e) => error!("There was an erroring sending form to nextcloud: {e}"),