feat: add email client
This commit is contained in:
parent
13db7853bd
commit
ac216925ff
7 changed files with 167 additions and 4 deletions
|
|
@ -21,6 +21,7 @@ pub struct Settings {
|
|||
pub struct EmailClientSettings {
|
||||
pub base_url: String,
|
||||
pub sender_email: String,
|
||||
pub authorization_token: Secret<String>,
|
||||
}
|
||||
|
||||
impl EmailClientSettings {
|
||||
|
|
|
|||
|
|
@ -1,19 +1,26 @@
|
|||
use crate::domain::SubscriberEmail;
|
||||
use reqwest::Client;
|
||||
use secrecy::{ExposeSecret, Secret};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EmailClient {
|
||||
http_client: Client,
|
||||
base_url: String,
|
||||
sender: SubscriberEmail,
|
||||
authorization_token: Secret<String>,
|
||||
}
|
||||
|
||||
impl EmailClient {
|
||||
pub fn new(base_url: String, sender: SubscriberEmail) -> Self {
|
||||
pub fn new(
|
||||
base_url: String,
|
||||
sender: SubscriberEmail,
|
||||
authorization_token: Secret<String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
http_client: Client::new(),
|
||||
base_url,
|
||||
sender,
|
||||
authorization_token,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -24,6 +31,69 @@ impl EmailClient {
|
|||
html_content: &str,
|
||||
text_content: &str,
|
||||
) -> Result<(), String> {
|
||||
todo!()
|
||||
// TODO: use `reqwest::Url::join` and change `base_url`'s type from `String` to `reqwest::Url`
|
||||
let url = format!("{}/email", self.base_url);
|
||||
let request_body = SendEmailRequest {
|
||||
from: self.sender.as_ref().to_owned(),
|
||||
to: recipient.as_ref().to_owned(),
|
||||
subject: subject.to_owned(),
|
||||
html_body: html_content.to_owned(),
|
||||
text_body: text_content.to_owned(),
|
||||
};
|
||||
let builder = self
|
||||
.http_client
|
||||
.post(&url)
|
||||
.header(
|
||||
"X-Postmark-Server-Token",
|
||||
self.authorization_token.expose_secret(),
|
||||
)
|
||||
.json(&request_body);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
#[derive(serde::Serialize)]
|
||||
struct SendEmailRequest {
|
||||
from: String,
|
||||
to: String,
|
||||
subject: String,
|
||||
html_body: String,
|
||||
text_body: String,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use crate::domain::SubscriberEmail;
|
||||
use crate::email_client::EmailClient;
|
||||
use fake::faker::internet::en::SafeEmail;
|
||||
use fake::faker::lorem::en::{Paragraph, Sentence};
|
||||
use fake::{Fake, Faker};
|
||||
use secrecy::Secret;
|
||||
use wiremock::matchers::any;
|
||||
use wiremock::{Mock, MockServer, ResponseTemplate};
|
||||
|
||||
#[tokio::test]
|
||||
async fn send_email_fires_a_request_to_base_url() {
|
||||
// Arrange
|
||||
let mock_server = MockServer::start().await;
|
||||
let sender = SubscriberEmail::parse(SafeEmail().fake()).unwrap();
|
||||
let email_client = EmailClient::new(mock_server.uri(), sender, Secret::new(Faker.fake()));
|
||||
|
||||
Mock::given(any())
|
||||
.respond_with(ResponseTemplate::new(200))
|
||||
.expect(1)
|
||||
.mount(&mock_server)
|
||||
.await;
|
||||
|
||||
let subscriber_email = SubscriberEmail::parse(SafeEmail().fake()).unwrap();
|
||||
let subject: String = Sentence(1..2).fake();
|
||||
let content: String = Paragraph(1..10).fake();
|
||||
|
||||
// Act
|
||||
let _ = email_client
|
||||
.send_email(subscriber_email, &subject, &content, &content)
|
||||
.await;
|
||||
|
||||
// Assert
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,11 @@ async fn main() {
|
|||
.email_client
|
||||
.sender()
|
||||
.expect("Invalid sender email address.");
|
||||
let email_client = EmailClient::new(configuration.email_client.base_url, sender_email);
|
||||
let email_client = EmailClient::new(
|
||||
configuration.email_client.base_url,
|
||||
sender_email,
|
||||
configuration.email_client.authorization_token,
|
||||
);
|
||||
startup::run(listener, connection_pool, email_client)
|
||||
.await
|
||||
.unwrap();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue