Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3344189
D8987.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
5 KB
Referenced Files
None
Subscribers
None
D8987.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D8987: [reports-service] Add functions to send e-mails
Attached
Detach File
Event Timeline
Log In to Comment