mirror of
https://github.com/LukeMathWalker/zero-to-production.git
synced 2024-12-18 05:56:35 +00:00
Share startup logic.
This commit is contained in:
parent
beeeacdafb
commit
55da2e2e2e
4 changed files with 59 additions and 58 deletions
|
@ -3,21 +3,21 @@ use serde_aux::field_attributes::deserialize_number_from_string;
|
||||||
use sqlx::postgres::{PgConnectOptions, PgSslMode};
|
use sqlx::postgres::{PgConnectOptions, PgSslMode};
|
||||||
use std::convert::{TryFrom, TryInto};
|
use std::convert::{TryFrom, TryInto};
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize, Clone)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
pub database: DatabaseSettings,
|
pub database: DatabaseSettings,
|
||||||
pub application: ApplicationSettings,
|
pub application: ApplicationSettings,
|
||||||
pub email_client: EmailClientSettings,
|
pub email_client: EmailClientSettings,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize, Clone)]
|
||||||
pub struct ApplicationSettings {
|
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize, Clone)]
|
||||||
pub struct DatabaseSettings {
|
pub struct DatabaseSettings {
|
||||||
pub username: String,
|
pub username: String,
|
||||||
pub password: String,
|
pub password: String,
|
||||||
|
@ -48,7 +48,7 @@ impl DatabaseSettings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize, Clone)]
|
||||||
pub struct EmailClientSettings {
|
pub struct EmailClientSettings {
|
||||||
pub base_url: String,
|
pub base_url: String,
|
||||||
pub sender_email: String,
|
pub sender_email: String,
|
||||||
|
|
29
src/main.rs
29
src/main.rs
|
@ -1,8 +1,5 @@
|
||||||
use sqlx::postgres::PgPoolOptions;
|
|
||||||
use std::net::TcpListener;
|
|
||||||
use zero2prod::configuration::get_configuration;
|
use zero2prod::configuration::get_configuration;
|
||||||
use zero2prod::email_client::EmailClient;
|
use zero2prod::startup::build;
|
||||||
use zero2prod::startup::run;
|
|
||||||
use zero2prod::telemetry::{get_subscriber, init_subscriber};
|
use zero2prod::telemetry::{get_subscriber, init_subscriber};
|
||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
|
@ -11,27 +8,7 @@ async fn main() -> std::io::Result<()> {
|
||||||
init_subscriber(subscriber);
|
init_subscriber(subscriber);
|
||||||
|
|
||||||
let configuration = get_configuration().expect("Failed to read configuration.");
|
let configuration = get_configuration().expect("Failed to read configuration.");
|
||||||
let connection_pool = PgPoolOptions::new()
|
let server = build(configuration).await?;
|
||||||
.connect_timeout(std::time::Duration::from_secs(2))
|
server.await?;
|
||||||
.connect_with(configuration.database.with_db())
|
|
||||||
.await
|
|
||||||
.expect("Failed to connect to Postgres.");
|
|
||||||
|
|
||||||
let sender_email = configuration
|
|
||||||
.email_client
|
|
||||||
.sender()
|
|
||||||
.expect("Invalid sender email address.");
|
|
||||||
let email_client = EmailClient::new(
|
|
||||||
configuration.email_client.base_url,
|
|
||||||
sender_email,
|
|
||||||
configuration.email_client.authorization_token,
|
|
||||||
);
|
|
||||||
|
|
||||||
let address = format!(
|
|
||||||
"{}:{}",
|
|
||||||
configuration.application.host, configuration.application.port
|
|
||||||
);
|
|
||||||
let listener = TcpListener::bind(address)?;
|
|
||||||
run(listener, connection_pool, email_client)?.await?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,44 @@
|
||||||
|
use crate::configuration::{DatabaseSettings, Settings};
|
||||||
use crate::email_client::EmailClient;
|
use crate::email_client::EmailClient;
|
||||||
use crate::routes::{health_check, subscribe};
|
use crate::routes::{health_check, subscribe};
|
||||||
use actix_web::dev::Server;
|
use actix_web::dev::Server;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
use actix_web::{web, App, HttpServer};
|
use actix_web::{web, App, HttpServer};
|
||||||
|
use sqlx::postgres::PgPoolOptions;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use std::net::TcpListener;
|
use std::net::TcpListener;
|
||||||
use tracing_actix_web::TracingLogger;
|
use tracing_actix_web::TracingLogger;
|
||||||
|
|
||||||
|
pub async fn build(configuration: Settings) -> Result<Server, std::io::Error> {
|
||||||
|
let connection_pool = get_connection_pool(&configuration.database)
|
||||||
|
.await
|
||||||
|
.expect("Failed to connect to Postgres.");
|
||||||
|
|
||||||
|
let sender_email = configuration
|
||||||
|
.email_client
|
||||||
|
.sender()
|
||||||
|
.expect("Invalid sender email address.");
|
||||||
|
let email_client = EmailClient::new(
|
||||||
|
configuration.email_client.base_url,
|
||||||
|
sender_email,
|
||||||
|
configuration.email_client.authorization_token,
|
||||||
|
);
|
||||||
|
|
||||||
|
let address = format!(
|
||||||
|
"{}:{}",
|
||||||
|
configuration.application.host, configuration.application.port
|
||||||
|
);
|
||||||
|
let listener = TcpListener::bind(address)?;
|
||||||
|
run(listener, connection_pool, email_client)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_connection_pool(configuration: &DatabaseSettings) -> Result<PgPool, sqlx::Error> {
|
||||||
|
PgPoolOptions::new()
|
||||||
|
.connect_timeout(std::time::Duration::from_secs(2))
|
||||||
|
.connect_with(configuration.with_db())
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
pub fn run(
|
pub fn run(
|
||||||
listener: TcpListener,
|
listener: TcpListener,
|
||||||
db_pool: PgPool,
|
db_pool: PgPool,
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
use sqlx::{Connection, Executor, PgConnection, PgPool};
|
use sqlx::{Connection, Executor, PgConnection, PgPool};
|
||||||
use std::net::TcpListener;
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use zero2prod::configuration::{get_configuration, DatabaseSettings};
|
use zero2prod::configuration::{get_configuration, DatabaseSettings};
|
||||||
use zero2prod::email_client::EmailClient;
|
use zero2prod::startup::{build, get_connection_pool};
|
||||||
use zero2prod::startup::run;
|
|
||||||
use zero2prod::telemetry::{get_subscriber, init_subscriber};
|
use zero2prod::telemetry::{get_subscriber, init_subscriber};
|
||||||
|
|
||||||
// Ensure that the `tracing` stack is only initialised once using `lazy_static`
|
// Ensure that the `tracing` stack is only initialised once using `lazy_static`
|
||||||
|
@ -21,35 +19,29 @@ pub struct TestApp {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn spawn_app() -> TestApp {
|
pub async fn spawn_app() -> TestApp {
|
||||||
// The first time `initialize` is invoked the code in `TRACING` is executed.
|
// Randomise configuration to ensure test isolation
|
||||||
// All other invocations will instead skip execution.
|
let configuration = {
|
||||||
lazy_static::initialize(&TRACING);
|
let mut c = get_configuration().expect("Failed to read configuration.");
|
||||||
|
// Use a different database for each test case
|
||||||
|
c.database.database_name = Uuid::new_v4().to_string();
|
||||||
|
// Use a random OS port
|
||||||
|
c.application.port = 0;
|
||||||
|
c
|
||||||
|
};
|
||||||
|
|
||||||
let listener = TcpListener::bind("127.0.0.1:0").expect("Failed to bind random port");
|
// Create and migrate the database
|
||||||
// We retrieve the port assigned to us by the OS
|
configure_database(&configuration.database).await;
|
||||||
let port = listener.local_addr().unwrap().port();
|
|
||||||
let address = format!("http://127.0.0.1:{}", port);
|
|
||||||
|
|
||||||
let mut configuration = get_configuration().expect("Failed to read configuration.");
|
// Launch the application as a background task
|
||||||
configuration.database.database_name = Uuid::new_v4().to_string();
|
let server = build(configuration.clone())
|
||||||
let connection_pool = configure_database(&configuration.database).await;
|
.await
|
||||||
|
.expect("Failed to build application.");
|
||||||
let sender_email = configuration
|
|
||||||
.email_client
|
|
||||||
.sender()
|
|
||||||
.expect("Invalid sender email address.");
|
|
||||||
let email_client = EmailClient::new(
|
|
||||||
configuration.email_client.base_url,
|
|
||||||
sender_email,
|
|
||||||
configuration.email_client.authorization_token,
|
|
||||||
);
|
|
||||||
|
|
||||||
let server =
|
|
||||||
run(listener, connection_pool.clone(), email_client).expect("Failed to bind address");
|
|
||||||
let _ = tokio::spawn(server);
|
let _ = tokio::spawn(server);
|
||||||
TestApp {
|
TestApp {
|
||||||
address,
|
address: todo!(),
|
||||||
db_pool: connection_pool,
|
db_pool: get_connection_pool(&configuration.database)
|
||||||
|
.await
|
||||||
|
.expect("Failed to connect to the database"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue