Page MenuHomePhabricator

D8987.diff
No OneTemporary

D8987.diff

diff --git a/services/reports/Cargo.lock b/services/reports/Cargo.lock
--- a/services/reports/Cargo.lock
+++ b/services/reports/Cargo.lock
@@ -369,6 +369,17 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
+[[package]]
+name = "async-trait"
+version = "0.1.73"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.29",
+]
+
[[package]]
name = "autocfg"
version = "1.1.0"
@@ -1791,6 +1802,23 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
+[[package]]
+name = "postmark"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "254a5dd703e0cb58b305d882618698682719141a09483868401ba3d0e689a96b"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "http",
+ "reqwest",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "typed-builder",
+ "url",
+]
+
[[package]]
name = "ppv-lite86"
version = "0.2.17"
@@ -1940,6 +1968,7 @@
"num-derive",
"num-traits",
"once_cell",
+ "postmark",
"serde",
"serde_json",
"serde_repr",
@@ -2320,6 +2349,26 @@
"windows-sys",
]
+[[package]]
+name = "thiserror"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.29",
+]
+
[[package]]
name = "thread_local"
version = "1.1.7"
@@ -2569,6 +2618,17 @@
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
+[[package]]
+name = "typed-builder"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64cba322cb9b7bc6ca048de49e83918223f35e7a86311267013afff257004870"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
[[package]]
name = "typenum"
version = "1.16.0"
diff --git a/services/reports/Cargo.toml b/services/reports/Cargo.toml
--- a/services/reports/Cargo.toml
+++ b/services/reports/Cargo.toml
@@ -24,6 +24,7 @@
num-traits = "0.2"
num-derive = "0.4"
once_cell = "1.17"
+postmark = { version = "0.8", features = ["reqwest"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_repr = "0.1"
diff --git a/services/reports/src/email/config.rs b/services/reports/src/email/config.rs
--- a/services/reports/src/email/config.rs
+++ b/services/reports/src/email/config.rs
@@ -41,6 +41,18 @@
pub mailing_groups: HashMap<MailingGroup, String>,
}
+impl EmailConfig {
+ pub fn recipient_for_report_type(
+ &self,
+ report_type: &ReportType,
+ ) -> Option<&str> {
+ self
+ .mailing_groups
+ .get(&report_type.into())
+ .map(String::as_str)
+ }
+}
+
impl FromStr for EmailConfig {
type Err = serde_json::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
diff --git a/services/reports/src/email/mod.rs b/services/reports/src/email/mod.rs
--- a/services/reports/src/email/mod.rs
+++ b/services/reports/src/email/mod.rs
@@ -1,2 +1,73 @@
+use postmark::{
+ api::email::{self, SendEmailBatchRequest},
+ reqwest::{PostmarkClient, PostmarkClientError},
+ Query,
+};
+use tracing::{debug, trace, warn};
+
+use crate::{
+ config::CONFIG,
+ report_types::{ReportID, ReportInput, ReportType},
+};
+
pub mod config;
mod template;
+
+pub type EmailError = postmark::QueryError<PostmarkClientError>;
+
+pub struct ReportEmail {
+ report_type: ReportType,
+ rendered_message: String,
+ subject: String,
+}
+
+pub fn prepare_email(
+ report_input: &ReportInput,
+ report_id: &ReportID,
+ user_id: Option<&str>,
+) -> ReportEmail {
+ let message =
+ template::render_email_for_report(report_input, report_id, user_id);
+ let subject = template::subject_for_report(report_input, user_id);
+ ReportEmail {
+ report_type: report_input.report_type,
+ rendered_message: message,
+ subject,
+ }
+}
+
+pub async fn send_emails(
+ emails: impl IntoIterator<Item = ReportEmail>,
+) -> Result<(), EmailError> {
+ let Some(email_config) = CONFIG.email_config() else {
+ debug!("E-mail config unavailable. Skipping sending e-mails");
+ return Ok(());
+ };
+
+ // it's cheap to build this every time
+ let client = PostmarkClient::builder()
+ .token(&email_config.postmark_token)
+ .build();
+
+ let requests: SendEmailBatchRequest = emails
+ .into_iter()
+ .filter_map(|item| {
+ let Some(recipient) = email_config.recipient_for_report_type(&item.report_type) else {
+ warn!("Recipient E-mail for {:?} not configured. Skipping", &item.report_type);
+ return None;
+ };
+
+ let email = email::SendEmailRequest::builder()
+ .from(&email_config.sender_email)
+ .to(recipient)
+ .body(email::Body::html(item.rendered_message))
+ .subject(item.subject)
+ .build();
+ Some(email)
+ })
+ .collect();
+
+ let responses = requests.execute(&client).await?;
+ trace!(?responses, "E-mails sent successfully.");
+ Ok(())
+}

File Metadata

Mime Type
text/plain
Expires
Sat, Nov 23, 4:25 AM (18 h, 27 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2568315
Default Alt Text
D8987.diff (5 KB)

Event Timeline