Skip to content

Commit

Permalink
ch9: refactor
Browse files Browse the repository at this point in the history
Signed-off-by: Jin Dong <djdongjin95@gmail.com>
  • Loading branch information
djdongjin committed Apr 5, 2024
1 parent 61a0bab commit 099f41b
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 22 deletions.
60 changes: 44 additions & 16 deletions src/domain/newsletter.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use crate::domain::SubscriberEmail;
use crate::{email_client::EmailClient, routes::error_chain_fmt};
use actix_web::{http::StatusCode, web, HttpResponse, ResponseError};
use anyhow::Context;
use sqlx::PgPool;

use crate::routes::error_chain_fmt;

#[derive(serde::Deserialize)]
pub struct BodyData {
title: String,
Expand All @@ -15,10 +16,6 @@ pub struct Content {
text: String,
}

struct ConfirmedSubscriber {
email: String,
}

#[derive(thiserror::Error)]
pub enum PublishError {
#[error(transparent)]
Expand All @@ -41,25 +38,56 @@ impl ResponseError for PublishError {
}

pub async fn publish_newsletter(
_body: web::Json<BodyData>,
body: web::Json<BodyData>,
pool: web::Data<PgPool>,
email_client: web::Data<EmailClient>,
) -> Result<HttpResponse, PublishError> {
let _subscribers = get_confirmed_subscribers(&pool).await?;
let subscribers = get_confirmed_subscribers(&pool).await?;

for sub in subscribers {
match sub {
Ok(sub) => {
email_client
.send_email(
&sub.email,
&body.title,
&body.content.html,
&body.content.text,
)
.await
.with_context(|| format!("Failed to send email to {}", sub.email))?;
}
Err(error) => {
tracing::warn!(
error.cause_chain = ?error,
"Skip a confirmed subscriber. \
Their stored contact details are invalid",
);
}
}
}

Ok(HttpResponse::Ok().finish())
}

struct ConfirmedSubscriber {
email: SubscriberEmail,
}

#[tracing::instrument(name = "Get confirmed subscribers", skip(pool))]
async fn get_confirmed_subscribers(
pool: &PgPool,
) -> Result<Vec<ConfirmedSubscriber>, anyhow::Error> {
// `query_as` maps results to the type specified as its first argument.
let confirmed_subscribers = sqlx::query_as!(
ConfirmedSubscriber,
r#"SELECT email FROM subscriptions WHERE status = 'confirmed'"#
)
.fetch_all(pool)
.await?;
) -> Result<Vec<Result<ConfirmedSubscriber, anyhow::Error>>, anyhow::Error> {
let confirmed_subscribers =
sqlx::query!(r#"SELECT email FROM subscriptions WHERE status = 'confirmed'"#)
.fetch_all(pool)
.await?
.into_iter()
.map(|r| match SubscriberEmail::parse(r.email) {
Ok(email) => Ok(ConfirmedSubscriber { email }),
Err(error) => Err(anyhow::anyhow!(error)),
})
.collect();

Ok(confirmed_subscribers)
}
6 changes: 6 additions & 0 deletions src/domain/subscriber_email.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ impl AsRef<str> for SubscriberEmail {
}
}

impl std::fmt::Display for SubscriberEmail {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}

#[cfg(test)]
mod tests {
use super::SubscriberEmail;
Expand Down
10 changes: 5 additions & 5 deletions src/email_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ impl EmailClient {

pub async fn send_email(
&self,
recipient: SubscriberEmail,
recipient: &SubscriberEmail,
subject: &str,
html_content: &str,
text_content: &str,
Expand Down Expand Up @@ -144,7 +144,7 @@ mod tests {

// Act
let _ = email_client
.send_email(email(), &subject(), &content(), &content())
.send_email(&email(), &subject(), &content(), &content())
.await;

// Assert via the mounted `Mock`.
Expand All @@ -164,7 +164,7 @@ mod tests {

// Act
let outcome = email_client
.send_email(email(), &subject(), &content(), &content())
.send_email(&email(), &subject(), &content(), &content())
.await;

// Assert
Expand All @@ -186,7 +186,7 @@ mod tests {

// Act
let outcome = email_client
.send_email(email(), &subject(), &content(), &content())
.send_email(&email(), &subject(), &content(), &content())
.await;

// Assert
Expand All @@ -208,7 +208,7 @@ mod tests {

// Act
let outcome = email_client
.send_email(email(), &subject(), &content(), &content())
.send_email(&email(), &subject(), &content(), &content())
.await;

// Assert
Expand Down
2 changes: 1 addition & 1 deletion src/routes/subscriptions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ pub async fn send_confirmation_email(
);

email_client
.send_email(form.email, "welcome", &html_body, &plain_body)
.send_email(&form.email, "welcome", &html_body, &plain_body)
.await
}

Expand Down

0 comments on commit 099f41b

Please sign in to comment.