brass/web/src/mail/notify_planned.rs

150 lines
3.7 KiB
Rust

use askama::Template;
use brass_db::models::{Event, Function, User};
use lettre::{
message::{Mailbox, MultiPart, SinglePart},
Address, AsyncTransport, Message,
};
use crate::{
filters,
mail::Mailer,
utils::{
ApplicationError,
DateTimeFormat::{DayMonthYear, HourMinute, WeekdayDayMonthYear},
},
};
impl Mailer {
pub async fn send_assignment_created_mail(
&self,
user: &User,
assigned_function: &Function,
event: &Event,
) -> Result<(), ApplicationError> {
let message = build(
&self.hostname,
&user.name,
&user.email,
assigned_function,
event,
)?;
self.transport.send(message).await?;
Ok(())
}
}
#[derive(Template)]
#[template(path = "emails/notify_planned.txt")]
struct NotifyPlannedMailTemplatePlain<'a> {
name: &'a str,
function: &'a Function,
event: &'a Event,
}
#[derive(Template)]
#[template(path = "emails/notify_planned.html")]
struct NotifyPlannedMailTemplateHtml<'a> {
name: &'a str,
function: &'a Function,
event: &'a Event,
}
fn build(
hostname: &str,
name: &str,
email: &str,
function: &Function,
event: &Event,
) -> Result<Message, ApplicationError> {
let plain = NotifyPlannedMailTemplatePlain {
name,
function,
event,
}
.to_string();
let html = NotifyPlannedMailTemplateHtml {
name,
function,
event,
}
.to_string();
let subject = format!(
"Brass: Einteilung bei {} am {} als {function}",
event.name,
event.start.format(DayMonthYear.into())
);
let sender_mailbox = Mailbox::new(
Some("noreply".to_string()),
Address::new("noreply", hostname)?,
);
let message = Message::builder()
.from(sender_mailbox.clone())
.reply_to(sender_mailbox)
.to(Mailbox::new(Some(name.to_string()), email.parse()?))
.subject(subject)
.multipart(
MultiPart::alternative()
.singlepart(SinglePart::plain(plain))
.singlepart(SinglePart::html(html)),
)
.unwrap();
Ok(message)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::utils::test_helper::{assert_mail_snapshot, NaiveDateTimeExt};
use brass_db::models::{Clothing, Location};
use chrono::NaiveDateTime;
use lettre::{transport::stub::StubTransport, Transport};
#[test]
fn build_mail_snapshot() {
let event = Event {
id: 1,
start: NaiveDateTime::from_ymd_and_hms(2025, 06, 01, 18, 30, 0).unwrap(),
end: NaiveDateTime::from_ymd_and_hms(2025, 06, 02, 4, 0, 0).unwrap(),
name: "Wave Gotik Treffen".to_string(),
location_id: 1,
location: Some(Location {
id: 1,
name: "agra Messe Leipzig".to_string(),
area_id: 1,
area: None,
}),
voluntary_wachhabender: false,
fuehrungsassistent_required: false,
amount_of_posten: 3,
clothing: Clothing {
id: 1,
name: "komplette PSA".to_string(),
},
canceled: false,
note: Some("Anfahrt Bereitstellungsraum über xyz...".to_string()),
};
let message = build(
"brasiwa-leipzig.de",
"Max Mustermann",
"max@example.com",
&Function::Posten,
&event,
)
.unwrap();
let sender = StubTransport::new_ok();
sender.send(&message).unwrap();
let messages = sender.messages();
let (_, sent_message) = messages.first().unwrap();
assert_mail_snapshot!(sent_message);
}
}