mirror of
https://github.com/LukeMathWalker/zero-to-production.git
synced 2024-12-18 22:16:40 +00:00
Get a 404.
This commit is contained in:
parent
01f1d8758e
commit
967b16b19b
6 changed files with 25 additions and 8 deletions
|
@ -1,4 +1,5 @@
|
||||||
application:
|
application:
|
||||||
host: 127.0.0.1
|
host: 127.0.0.1
|
||||||
|
url: "http://127.0.0.1"
|
||||||
database:
|
database:
|
||||||
require_ssl: false
|
require_ssl: false
|
||||||
|
|
|
@ -15,6 +15,7 @@ pub struct ApplicationSettings {
|
||||||
#[serde(deserialize_with = "deserialize_number_from_string")]
|
#[serde(deserialize_with = "deserialize_number_from_string")]
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
pub host: String,
|
pub host: String,
|
||||||
|
pub url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize, Clone)]
|
#[derive(serde::Deserialize, Clone)]
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::domain::{NewSubscriber, SubscriberEmail, SubscriberName};
|
use crate::domain::{NewSubscriber, SubscriberEmail, SubscriberName};
|
||||||
use crate::email_client::EmailClient;
|
use crate::email_client::EmailClient;
|
||||||
|
use crate::startup::ApplicationUrl;
|
||||||
use actix_web::{web, HttpResponse};
|
use actix_web::{web, HttpResponse};
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
@ -24,7 +25,7 @@ impl TryInto<NewSubscriber> for FormData {
|
||||||
|
|
||||||
#[tracing::instrument(
|
#[tracing::instrument(
|
||||||
name = "Adding a new subscriber",
|
name = "Adding a new subscriber",
|
||||||
skip(form, pool, email_client),
|
skip(form, pool, email_client, application_url),
|
||||||
fields(
|
fields(
|
||||||
email = %form.email,
|
email = %form.email,
|
||||||
name = %form.name
|
name = %form.name
|
||||||
|
@ -34,6 +35,7 @@ pub async fn subscribe(
|
||||||
form: web::Form<FormData>,
|
form: web::Form<FormData>,
|
||||||
pool: web::Data<PgPool>,
|
pool: web::Data<PgPool>,
|
||||||
email_client: web::Data<EmailClient>,
|
email_client: web::Data<EmailClient>,
|
||||||
|
application_url: web::Data<ApplicationUrl>,
|
||||||
) -> Result<HttpResponse, HttpResponse> {
|
) -> Result<HttpResponse, HttpResponse> {
|
||||||
let new_subscriber = form
|
let new_subscriber = form
|
||||||
.0
|
.0
|
||||||
|
@ -43,19 +45,20 @@ pub async fn subscribe(
|
||||||
.await
|
.await
|
||||||
.map_err(|_| HttpResponse::InternalServerError().finish())?;
|
.map_err(|_| HttpResponse::InternalServerError().finish())?;
|
||||||
// We are swallowing the error for the time being.
|
// We are swallowing the error for the time being.
|
||||||
let _ = send_confirmation_email(&email_client, new_subscriber).await;
|
let _ = send_confirmation_email(&email_client, new_subscriber, &application_url.0).await;
|
||||||
Ok(HttpResponse::Ok().finish())
|
Ok(HttpResponse::Ok().finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(
|
#[tracing::instrument(
|
||||||
name = "Send a confirmation email to a new subscriber",
|
name = "Send a confirmation email to a new subscriber",
|
||||||
skip(email_client, new_subscriber)
|
skip(email_client, new_subscriber, application_url)
|
||||||
)]
|
)]
|
||||||
pub async fn send_confirmation_email(
|
pub async fn send_confirmation_email(
|
||||||
email_client: &EmailClient,
|
email_client: &EmailClient,
|
||||||
new_subscriber: NewSubscriber,
|
new_subscriber: NewSubscriber,
|
||||||
|
application_url: &str,
|
||||||
) -> Result<(), reqwest::Error> {
|
) -> Result<(), reqwest::Error> {
|
||||||
let confirmation_link = "https://my-api.com/subscriptions/confirm";
|
let confirmation_link = format!("{}/subscriptions/confirm", application_url);
|
||||||
let plain_body = format!(
|
let plain_body = format!(
|
||||||
"Welcome to our newsletter!\nVisit {} to confirm your subscription.",
|
"Welcome to our newsletter!\nVisit {} to confirm your subscription.",
|
||||||
confirmation_link
|
confirmation_link
|
||||||
|
|
|
@ -36,7 +36,12 @@ impl Application {
|
||||||
);
|
);
|
||||||
let listener = TcpListener::bind(&address)?;
|
let listener = TcpListener::bind(&address)?;
|
||||||
let port = listener.local_addr().unwrap().port();
|
let port = listener.local_addr().unwrap().port();
|
||||||
let server = run(listener, connection_pool, email_client)?;
|
let server = run(
|
||||||
|
listener,
|
||||||
|
connection_pool,
|
||||||
|
email_client,
|
||||||
|
configuration.application.url,
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(Self { port, server })
|
Ok(Self { port, server })
|
||||||
}
|
}
|
||||||
|
@ -57,10 +62,13 @@ pub async fn get_connection_pool(configuration: &DatabaseSettings) -> Result<PgP
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ApplicationUrl(pub String);
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
listener: TcpListener,
|
listener: TcpListener,
|
||||||
db_pool: PgPool,
|
db_pool: PgPool,
|
||||||
email_client: EmailClient,
|
email_client: EmailClient,
|
||||||
|
application_url: String,
|
||||||
) -> Result<Server, std::io::Error> {
|
) -> Result<Server, std::io::Error> {
|
||||||
let db_pool = Data::new(db_pool);
|
let db_pool = Data::new(db_pool);
|
||||||
let email_client = Data::new(email_client);
|
let email_client = Data::new(email_client);
|
||||||
|
@ -71,6 +79,7 @@ fn run(
|
||||||
.route("/subscriptions", web::post().to(subscribe))
|
.route("/subscriptions", web::post().to(subscribe))
|
||||||
.app_data(db_pool.clone())
|
.app_data(db_pool.clone())
|
||||||
.app_data(email_client.clone())
|
.app_data(email_client.clone())
|
||||||
|
.data(ApplicationUrl(application_url.clone()))
|
||||||
})
|
})
|
||||||
.listen(listener)?
|
.listen(listener)?
|
||||||
.run();
|
.run();
|
||||||
|
|
|
@ -33,6 +33,8 @@ impl TestApp {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn spawn_app() -> TestApp {
|
pub async fn spawn_app() -> TestApp {
|
||||||
|
lazy_static::initialize(&TRACING);
|
||||||
|
|
||||||
// Launch a mock server to stand in for Postmark's API
|
// Launch a mock server to stand in for Postmark's API
|
||||||
let email_server = MockServer::start().await;
|
let email_server = MockServer::start().await;
|
||||||
|
|
||||||
|
|
|
@ -118,15 +118,16 @@ async fn the_link_returned_by_subscribe_sets_a_subscriber_as_confirmed() {
|
||||||
};
|
};
|
||||||
let confirmation_link = Url::parse(&get_link(&body["HtmlBody"].as_str().unwrap())).unwrap();
|
let confirmation_link = Url::parse(&get_link(&body["HtmlBody"].as_str().unwrap())).unwrap();
|
||||||
// Let's make sure we don't call random APIs on the web
|
// Let's make sure we don't call random APIs on the web
|
||||||
assert_eq!(confirmation_link.domain().unwrap(), "127.0.0.1");
|
assert_eq!(confirmation_link.host_str().unwrap(), "127.0.0.1");
|
||||||
|
// Let's rewrite the URL to include the port
|
||||||
|
let confirmation_link = format!("{}{}", app.address, confirmation_link.path());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
let response = reqwest::get(confirmation_link)
|
let response = reqwest::get(&confirmation_link)
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.error_for_status()
|
.error_for_status()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
dbg!(&response);
|
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
let saved = sqlx::query!(r#"SELECT status FROM subscriptions WHERE name = 'le guin' AND email = 'ursula_le_guin@gmail.com'"#,)
|
let saved = sqlx::query!(r#"SELECT status FROM subscriptions WHERE name = 'le guin' AND email = 'ursula_le_guin@gmail.com'"#,)
|
||||||
|
|
Loading…
Reference in a new issue