Skip to content

Commit

Permalink
added new mail template to rust; almost complete
Browse files Browse the repository at this point in the history
  • Loading branch information
worldofjoni committed Jun 17, 2024
1 parent 9c0b95a commit c9f1984
Show file tree
Hide file tree
Showing 14 changed files with 122 additions and 102 deletions.
21 changes: 11 additions & 10 deletions backend/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ license = "MIT"

[dependencies]
async-trait = "0.1.68"
chrono = "0.4.26"
chrono = { version = "0.4.26", features = ["serde"] }
thiserror = "1.0.40"
uuid = "1.4.0"
axum = { version = "0.6.18", features = [
Expand Down Expand Up @@ -55,7 +55,6 @@ sqlx = { version = "0.7", features = [
"macros",
] }
lettre = "0.11.1"
string_template = "0.2.1"
lazy_static = "1.4.0"
colored = "2.0.4"
image = "0.24.0"
Expand All @@ -67,6 +66,7 @@ hyper = { version = "0.14" }
mime = "0.3.17"
hmac = "0.12.1"
multer = { version = "3.0.0", features = ["tokio-io"] }
minijinja = "2.0.2"

[dev-dependencies]
serial_test = "3.0.0"
Expand Down
17 changes: 13 additions & 4 deletions backend/src/interface/admin_notification.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
//! This interface allows administrators to be notified of reporting requests.

use async_trait::async_trait;
use serde::Serialize;

use crate::util::{ReportReason, Uuid};
use crate::util::{Date, ReportReason, Uuid};

/// Interface for notification of administrators.
#[async_trait]
Expand All @@ -11,7 +12,7 @@ pub trait AdminNotification: Sync + Send {
async fn notify_admin_image_report(&self, info: ImageReportInfo);
}

#[derive(Debug)]
#[derive(Debug, Serialize)]
/// Structure containing all information about the reporting of an image.
pub struct ImageReportInfo {
/// Reason for the report.
Expand All @@ -29,11 +30,19 @@ pub struct ImageReportInfo {
/// Number of negative ratings for this image.
pub negative_rating_count: u32,
/// Image rank after which the images are sorted when shown to the user.
pub get_image_rank: f32,
pub image_rank: f32,
/// Number of times this image would have to be reported to automatically get hidden (at the current date).
pub report_barrier: u32,
/// User that reported the image.
pub client_id: Uuid,
/// The age of the image in days
/// The age of the image in days.
pub image_age: i64,
/// Name of the meal this image belongs to.
pub meal_name: String,
/// Id of the meal this image belongs to.
pub meal_id: Uuid,
/// Date and time this image got reported.
pub report_date: Date,
/// list of urls of other images of the same meal.
pub other_image_urls: Vec<String>,
}
2 changes: 2 additions & 0 deletions backend/src/interface/persistent_data/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ pub struct Image {
pub upload_date: Date,
/// Amount of open report request related to that image.
pub report_count: u32,
/// Id of the meal this image belongs to.
pub meal_id: Uuid,
}

/// This struct contains all environmental information. co2 in grams, water in litres
Expand Down
4 changes: 3 additions & 1 deletion backend/src/layer/data/database/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ impl CommandDataAccess for PersistentCommandData {
let record = sqlx::query!(
r#"
SELECT approved, link_date as upload_date, report_count,
upvotes, downvotes, image_id, rank
upvotes, downvotes, image_id, rank, food_id
FROM image_detail
WHERE image_id = $1
ORDER BY image_id
Expand All @@ -39,6 +39,7 @@ impl CommandDataAccess for PersistentCommandData {
downvotes: u32::try_from(null_error!(record.downvotes))?,
upvotes: u32::try_from(null_error!(record.upvotes))?,
id: null_error!(record.image_id),
meal_id: null_error!(record.food_id),
})
}

Expand Down Expand Up @@ -190,6 +191,7 @@ mod test {
approved: false,
upload_date: Local::now().date_naive(),
report_count: 0,
meal_id: Uuid::default(),
}
}

Expand Down
4 changes: 3 additions & 1 deletion backend/src/layer/data/database/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ impl RequestDataAccess for PersistentRequestData {
sqlx::query!(
"
SELECT image_id, rank, id as hoster_id, url, upvotes, downvotes,
approved, report_count, link_date
approved, report_count, link_date, food_id
FROM (
--- not reported by user
SELECT image_id
Expand All @@ -226,6 +226,7 @@ impl RequestDataAccess for PersistentRequestData {
approved: null_error!(r.approved),
report_count: u32::try_from(null_error!(r.report_count))?,
upload_date: null_error!(r.link_date),
meal_id: null_error!(r.food_id),
})
})
.collect::<Result<Vec<_>>>()
Expand Down Expand Up @@ -568,6 +569,7 @@ mod tests {
upvotes: 0,
upload_date: Local::now().date_naive(),
report_count: 0,
meal_id: Uuid::default(),
};
let image2 = Image {
id: Uuid::parse_str("76b904fe-d0f1-4122-8832-d0e21acab86d").unwrap(),
Expand Down
82 changes: 47 additions & 35 deletions backend/src/layer/data/mail/mail_sender.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,28 @@
use std::fmt::Debug;

use async_trait::async_trait;
use minijinja::{context, Environment, Value};
use thiserror::Error;

use lettre::{
address::AddressError, message::Mailbox, transport::smtp::authentication::Credentials, Address,
Message, SmtpTransport, Transport,
address::AddressError,
message::{Mailbox, MaybeString, SinglePart},
transport::smtp::authentication::Credentials,
Address, Message, SmtpTransport, Transport,
};

use crate::{
interface::admin_notification::{AdminNotification, ImageReportInfo},
layer::data::mail::mail_info::MailInfo,
};

use string_template::Template;
use tracing::{error, info};

/// Result returned when sending emails, potentially containing a [`MailError`].
pub type MailResult<T> = std::result::Result<T, MailError>;

const REPORT_TEMPLATE: &str = include_str!("./template.txt");
const REPORT_TEMPLATE: &str = include_str!("./template/template.html");
const REPORT_CSS: &str = include_str!("./template/output.css");
const SENDER_NAME: &str = "MensaKa";
const RECEIVER_NAME: &str = "Administrator";
const MAIL_SUBJECT: &str = "An image was reported and requires your review";
Expand Down Expand Up @@ -77,7 +80,7 @@ impl MailSender {
.from(sender)
.to(reciever)
.subject(MAIL_SUBJECT)
.body(report)?;
.singlepart(SinglePart::html(MaybeString::String(report)))?;
self.mailer.send(&email)?;
info!(
?info,
Expand All @@ -97,39 +100,33 @@ impl MailSender {
}

fn get_report(info: &ImageReportInfo) -> String {
let info_array_map = [
("image_link", &info.image_link as &dyn ToString),
("image_id", &info.image_id),
("report_count", &info.report_count),
("reason", &info.reason),
("image_got_hidden", &info.image_got_hidden),
("positive_rating_count", &info.positive_rating_count),
("negative_rating_count", &info.negative_rating_count),
("get_image_rank", &info.get_image_rank),
("report_barrier", &info.report_barrier),
("client_id", &info.client_id),
("image_age", &info.image_age),
];

let info_map = info_array_map
.into_iter()
.map(|(k, v)| (k, v.to_string()))
.collect::<Vec<_>>();
let info_map = info_map.iter().map(|(k, v)| (*k, v.as_str())).collect();

Template::new(REPORT_TEMPLATE).render(&info_map)
let env = Environment::new();
let template = env
.template_from_str(REPORT_TEMPLATE)
.expect("template always preset");

template
.render(context!(
css => REPORT_CSS,
delete_url => "#", // todo
verify_url => "#", // todo
..Value::from_serialize(info),
))
.expect("all arguments provided at compile time")
}
}

#[cfg(test)]
mod test {
#![allow(clippy::unwrap_used)]
use super::REPORT_CSS;
use crate::{
interface::admin_notification::{AdminNotification, ImageReportInfo},
layer::data::mail::mail_info::MailInfo,
layer::data::mail::mail_sender::MailSender,
util::Uuid,
};
use chrono::Local;
use dotenvy;
use std::env::{self, VarError};
use tracing_test::traced_test;
Expand All @@ -140,16 +137,24 @@ mod test {
const SMTP_PASSWORD_ENV_NAME: &str = "SMTP_PASSWORD";
const ADMIN_EMAIL_ENV_NAME: &str = "ADMIN_EMAIL";

#[tokio::test]
#[ignore]
async fn test_print_report() {
let info = get_report_info();
let report = MailSender::get_report(&info);
println!("{report}");
}

#[tokio::test]
async fn test_get_report() {
let info = get_report_info();
let report = MailSender::get_report(&info).replace("\r\n", "\n");
assert!(
!report.contains("{{"),
!report.contains("{{ "),
"the template must not contain any formatting"
);
assert!(
!report.contains("}}"),
!report.contains(" }}"),
"the template must not contain any formatting"
);
assert!(
Expand All @@ -164,10 +169,10 @@ mod test {
report.contains(info.report_count.to_string().as_str()),
"the template must contain all of the information from the report info"
);
assert!(
report.contains(info.image_got_hidden.to_string().as_str()),
"the template must contain all of the information from the report info"
);
// assert!(
// report.contains(info.image_got_hidden.to_string().as_str()),
// "the template must contain all of the information from the report info"
// );
assert!(
report.contains(info.positive_rating_count.to_string().as_str()),
"the template must contain all of the information from the report info"
Expand All @@ -177,7 +182,7 @@ mod test {
"the template must contain all of the information from the report info"
);
assert!(
report.contains(info.get_image_rank.to_string().as_str()),
report.contains(info.image_rank.to_string().as_str()),
"the template must contain all of the information from the report info"
);
assert!(
Expand All @@ -192,6 +197,9 @@ mod test {
report.contains(info.image_age.to_string().as_str()),
"the template must contain all of the information from the report info"
);
assert!(
report.contains(REPORT_CSS), "Report css must be included. maybe auto-formatting destroyed the braces in template.html?"
);
}

#[tokio::test]
Expand Down Expand Up @@ -227,14 +235,18 @@ mod test {
reason: crate::util::ReportReason::Advert,
image_got_hidden: true,
image_id: Uuid::default(),
image_link: String::from("www.test.com"),
image_link: String::from("https://picsum.photos/200/300"),
report_count: 1,
positive_rating_count: 10,
negative_rating_count: 20,
get_image_rank: 1.0,
image_rank: 1.0,
report_barrier: 1,
client_id: Uuid::default(),
image_age: 1,
meal_id: Uuid::default(),
meal_name: "Happy Meal".into(),
report_date: Local::now().date_naive(),
other_image_urls: vec!["https://picsum.photos/200/300".into()],
}
}

Expand Down
Loading

0 comments on commit c9f1984

Please sign in to comment.