Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ once_cell = "1.19.0"
quickcheck = "1.0.3"
quickcheck_macros = "1.0.0"
rand = "0.8.5"
reqwest = { version = "0.11.23", features = ["json", "rustls-tls"] }
secrecy = { version = "0.8.0", features = ["serde"] }
serde = { version = "1", features = ["derive"] }
serde-aux = "4.3.1"
Expand Down Expand Up @@ -49,4 +50,3 @@ features = [
]

[dev-dependencies]
reqwest = "0.11.23"
4 changes: 4 additions & 0 deletions config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@ database:
port: 5433
name: "newsletter"
require_ssl: false
email_client:
base_url: "localhost"
auth_token: "testing"
sender_email: "test@gmail.com"
3 changes: 3 additions & 0 deletions config/prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ app:
host: 0.0.0.0
database:
require_ssl: true
email_client:
base_url: "https://api.sendgrid.com/v3"
sender_email: "test@gmail.com"
25 changes: 18 additions & 7 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ use sqlx::postgres::PgConnectOptions;
use sqlx::postgres::PgSslMode;
use sqlx::ConnectOptions;

use crate::domain::SubscriberEmail;

#[derive(Debug, serde::Deserialize)]
pub struct Settings {
pub app: AppSettings,
pub database: DatabaseSettings,
pub email_client: EmailClientSettings,
}

#[derive(Debug, serde::Deserialize)]
Expand All @@ -29,6 +32,19 @@ pub struct DatabaseSettings {
pub require_ssl: bool,
}

#[derive(Debug, serde::Deserialize)]
pub struct EmailClientSettings {
pub base_url: String,
pub auth_token: Secret<String>,
pub sender_email: String,
}

impl EmailClientSettings {
pub fn sender(&self) -> Result<SubscriberEmail, String> {
SubscriberEmail::parse(self.sender_email.clone())
}
}

impl DatabaseSettings {
pub fn with_db(&self) -> PgConnectOptions {
self.without_db()
Expand Down Expand Up @@ -82,8 +98,7 @@ impl TryFrom<String> for AppEnv {
}

pub fn get_config() -> Result<Settings, config::ConfigError> {
let base_path =
std::env::current_dir().expect("Failed to determine the current directory");
let base_path = std::env::current_dir().expect("Failed to determine the current directory");
let config_path = base_path.join("config");

let app_env: AppEnv = std::env::var("APP_ENV")
Expand All @@ -94,11 +109,7 @@ pub fn get_config() -> Result<Settings, config::ConfigError> {
Config::builder()
.add_source(config::File::from(config_path.join("config")))
.add_source(config::File::from(config_path.join(app_env.as_str())))
.add_source(
config::Environment::default()
.try_parsing(true)
.separator("_"),
)
.add_source(config::Environment::default().try_parsing(true).separator("_"))
.build()?
.try_deserialize()
}
31 changes: 31 additions & 0 deletions src/email_client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use crate::domain::SubscriberEmail;
use reqwest::Client;
use secrecy::Secret;

pub struct EmailClient {
http_client: Client,
base_url: String,
auth_token: Secret<String>,
sender: SubscriberEmail,
}

impl EmailClient {
pub fn new(base_url: String, auth_token: Secret<String>, sender: SubscriberEmail) -> Self {
Self {
http_client: Client::new(),
base_url,
auth_token,
sender,
}
}

pub async fn send_email(
&self,
recipient: SubscriberEmail,
subject: &str,
html_content: &str,
text_content: &str,
) -> Result<(), String> {
todo!()
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod config;
pub mod domain;
pub mod email_client;
pub mod routes;
pub mod startup;
pub mod telemetry;
11 changes: 10 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use sqlx::postgres::PgPoolOptions;
use std::net::TcpListener;
use zero2prod::config::get_config;
use zero2prod::email_client::EmailClient;
use zero2prod::startup::run;
use zero2prod::telemetry::{get_subscriber, init_subscriber};

Expand All @@ -17,5 +18,13 @@ async fn main() -> std::io::Result<()> {
.acquire_timeout(std::time::Duration::from_secs(2))
.connect_lazy_with(config.database.with_db());

run(listener, connection_pool)?.await
let sender_email = config.email_client.sender().expect("Invalid sender email address.");

let email_client = EmailClient::new(
config.email_client.base_url,
config.email_client.auth_token,
sender_email,
);

run(listener, connection_pool, email_client)?.await
}
8 changes: 6 additions & 2 deletions src/startup.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::email_client::EmailClient;
use crate::routes::*;
use actix_web::dev::Server;
use actix_web::web::Data;
Expand All @@ -6,14 +7,17 @@ use sqlx::PgPool;
use std::net::TcpListener;
use tracing_actix_web::TracingLogger;

pub fn run(listener: TcpListener, db_pool: PgPool) -> Result<Server, std::io::Error> {
pub fn run(listener: TcpListener, db_pool: PgPool, email_client: EmailClient) -> Result<Server, std::io::Error> {
let db_pool = Data::new(db_pool);
let email_client = Data::new(email_client);

let server = HttpServer::new(move || {
App::new()
.wrap(TracingLogger::default())
.app_data(db_pool.clone())
.route("/health-check", web::get().to(health_check))
.route("/subscriptions", web::post().to(subscribe))
.app_data(db_pool.clone())
.app_data(email_client.clone())
})
.listen(listener)?
.run();
Expand Down
8 changes: 8 additions & 0 deletions tests/health_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,14 @@ async fn spawn_app() -> TestApp {
let port = listener.local_addr().unwrap().port();
let address = format!("http://127.0.0.1:{}", port);

let sender_email = config.email_client.sender().expect("Invalid sender email address.");

let email_client = EmailClient::new(
config.email_client.base_url,
config.email_client.auth_token,
sender_email,
);

let server = startup::run(listener, db_pool.clone()).expect("Failed to bind address");
let _ = tokio::spawn(server);

Expand Down