feat: finished chapter 5

This commit is contained in:
Kristofers Solo 2024-03-24 21:04:08 +02:00
parent 3ba9c5f3f8
commit af876c680b
8 changed files with 83 additions and 213 deletions

191
Cargo.lock generated
View File

@ -243,18 +243,11 @@ version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7328b20597b53c2454f0b1919720c25c7339051c02b72b7e05409e00b14132be" checksum = "7328b20597b53c2454f0b1919720c25c7339051c02b72b7e05409e00b14132be"
dependencies = [ dependencies = [
"async-trait",
"convert_case",
"json5",
"lazy_static", "lazy_static",
"nom", "nom",
"pathdiff", "pathdiff",
"ron",
"rust-ini",
"serde", "serde",
"serde_json",
"toml", "toml",
"yaml-rust",
] ]
[[package]] [[package]]
@ -263,35 +256,6 @@ version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
[[package]]
name = "const-random"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359"
dependencies = [
"const-random-macro",
]
[[package]]
name = "const-random-macro"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
dependencies = [
"getrandom",
"once_cell",
"tiny-keccak",
]
[[package]]
name = "convert_case"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
dependencies = [
"unicode-segmentation",
]
[[package]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.9.4" version = "0.9.4"
@ -347,12 +311,6 @@ version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
[[package]]
name = "crunchy"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]] [[package]]
name = "crypto-common" name = "crypto-common"
version = "0.1.6" version = "0.1.6"
@ -395,15 +353,6 @@ dependencies = [
"subtle", "subtle",
] ]
[[package]]
name = "dlv-list"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f"
dependencies = [
"const-random",
]
[[package]] [[package]]
name = "dotenvy" name = "dotenvy"
version = "0.15.7" version = "0.15.7"
@ -642,12 +591,6 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "hashbrown"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.14.3" version = "0.14.3"
@ -664,7 +607,7 @@ version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
dependencies = [ dependencies = [
"hashbrown 0.14.3", "hashbrown",
] ]
[[package]] [[package]]
@ -858,7 +801,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown 0.14.3", "hashbrown",
] ]
[[package]] [[package]]
@ -891,17 +834,6 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "json5"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1"
dependencies = [
"pest",
"pest_derive",
"serde",
]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
@ -934,12 +866,6 @@ dependencies = [
"vcpkg", "vcpkg",
] ]
[[package]]
name = "linked-hash-map"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.4.13" version = "0.4.13"
@ -1185,16 +1111,6 @@ dependencies = [
"vcpkg", "vcpkg",
] ]
[[package]]
name = "ordered-multimap"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ed8acf08e98e744e5384c8bc63ceb0364e68a6854187221c18df61c4797690e"
dependencies = [
"dlv-list",
"hashbrown 0.13.2",
]
[[package]] [[package]]
name = "overload" name = "overload"
version = "0.1.1" version = "0.1.1"
@ -1251,51 +1167,6 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pest"
version = "2.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56f8023d0fb78c8e03784ea1c7f3fa36e68a723138990b8d5a47d916b651e7a8"
dependencies = [
"memchr",
"thiserror",
"ucd-trie",
]
[[package]]
name = "pest_derive"
version = "2.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0d24f72393fd16ab6ac5738bc33cdb6a9aa73f8b902e8fe29cf4e67d7dd1026"
dependencies = [
"pest",
"pest_generator",
]
[[package]]
name = "pest_generator"
version = "2.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdc17e2a6c7d0a492f0158d7a4bd66cc17280308bbaff78d5bef566dca35ab80"
dependencies = [
"pest",
"pest_meta",
"proc-macro2",
"quote",
"syn 2.0.55",
]
[[package]]
name = "pest_meta"
version = "2.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "934cd7631c050f4674352a6e835d5f6711ffbfb9345c2fc0107155ac495ae293"
dependencies = [
"once_cell",
"pest",
"sha2",
]
[[package]] [[package]]
name = "pin-project" name = "pin-project"
version = "1.1.5" version = "1.1.5"
@ -1525,18 +1396,6 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "ron"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"
dependencies = [
"base64",
"bitflags 2.5.0",
"serde",
"serde_derive",
]
[[package]] [[package]]
name = "rsa" name = "rsa"
version = "0.9.6" version = "0.9.6"
@ -1557,16 +1416,6 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "rust-ini"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e2a3bcec1f113553ef1c88aae6c020a369d03d55b58de9869a0908930385091"
dependencies = [
"cfg-if",
"ordered-multimap",
]
[[package]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.23" version = "0.1.23"
@ -1695,6 +1544,17 @@ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]]
name = "serde-aux"
version = "4.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d2e8bfba469d06512e11e3311d4d051a4a387a5b42d010404fecf3200321c95"
dependencies = [
"chrono",
"serde",
"serde_json",
]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.197" version = "1.0.197"
@ -2203,15 +2063,6 @@ dependencies = [
"time-core", "time-core",
] ]
[[package]]
name = "tiny-keccak"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
dependencies = [
"crunchy",
]
[[package]] [[package]]
name = "tinyvec" name = "tinyvec"
version = "1.6.0" version = "1.6.0"
@ -2474,12 +2325,6 @@ version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "ucd-trie"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
[[package]] [[package]]
name = "unicode-bidi" name = "unicode-bidi"
version = "0.3.15" version = "0.3.15"
@ -2859,15 +2704,6 @@ dependencies = [
"windows-sys 0.48.0", "windows-sys 0.48.0",
] ]
[[package]]
name = "yaml-rust"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]
[[package]] [[package]]
name = "zero2prod" name = "zero2prod"
version = "0.1.0" version = "0.1.0"
@ -2879,6 +2715,7 @@ dependencies = [
"reqwest", "reqwest",
"secrecy", "secrecy",
"serde", "serde",
"serde-aux",
"sqlx", "sqlx",
"tokio", "tokio",
"tower-http", "tower-http",

View File

@ -16,9 +16,9 @@ name = "zero2prod"
[dependencies] [dependencies]
axum = "0.7" axum = "0.7"
chrono = { version = "0.4", features = ["serde", "clock"] } chrono = { version = "0.4", features = ["serde", "clock"] }
config = "0.14" config = { version = "0.14", features = ["toml"], default-features = false }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
sqlx = { version = "0.7", features = [ sqlx = { version = "0.7", default-features = false, features = [
"runtime-tokio", "runtime-tokio",
"tls-rustls", "tls-rustls",
"macros", "macros",
@ -35,6 +35,7 @@ tower-http = { version = "0.5", features = ["trace"] }
tracing-bunyan-formatter = "0.3" tracing-bunyan-formatter = "0.3"
tracing-log = "0.2" tracing-log = "0.2"
secrecy = { version = "0.8", features = ["serde"] } secrecy = { version = "0.8", features = ["serde"] }
serde-aux = "4"
[dev-dependencies] [dev-dependencies]
reqwest = "0.12" reqwest = "0.12"

View File

@ -1,2 +1,5 @@
[application] [application]
host = "127.0.0.1" host = "127.0.0.1"
[database]
require_ssl = false

View File

@ -1,2 +1,5 @@
[application] [application]
host = "0.0.0.0" host = "0.0.0.0"
[database]
require_ssl = true

View File

@ -2,6 +2,22 @@ name: zero2prod
region: ams3 region: ams3
services: services:
- name: zero2prod - name: zero2prod
envs:
- key: APP_DATABASE__USERNAME
scope: RUN_TIME
value: ${newsletter.USERNAME}
- key: APP_DATABASE__PASSWORD
scope: RUN_TIME
value: ${newsletter.PASSWORD}
- key: APP_DATABASE__HOST
scope: RUN_TIME
value: ${newsletter.HOSTNAME}
- key: APP_DATABASE__PORT
scope: RUN_TIME
value: ${newsletter.PORT}
- key: APP_DATABASE__DATABASE_NAME
scope: RUN_TIME
value: ${newsletter.DATABASE}
dockerfile_path: Dockerfile dockerfile_path: Dockerfile
source_dir: . source_dir: .
github: github:

View File

@ -2,6 +2,11 @@ use std::fmt::Display;
use secrecy::{ExposeSecret, Secret}; use secrecy::{ExposeSecret, Secret};
use serde::Deserialize; use serde::Deserialize;
use serde_aux::field_attributes::deserialize_number_from_string;
use sqlx::{
postgres::{PgConnectOptions, PgSslMode},
ConnectOptions,
};
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct Settings { pub struct Settings {
@ -13,36 +18,24 @@ pub struct Settings {
pub struct DatabaseSettings { pub struct DatabaseSettings {
pub username: String, pub username: String,
pub password: Secret<String>, pub password: Secret<String>,
#[serde(deserialize_with = "deserialize_number_from_string")]
pub port: u16, pub port: u16,
pub host: String, pub host: String,
pub database_name: String, pub database_name: String,
pub require_ssl: bool,
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct ApplicationSettings { pub struct ApplicationSettings {
#[serde(deserialize_with = "deserialize_number_from_string")]
pub port: u16, pub port: u16,
pub host: String, pub host: String,
} }
impl DatabaseSettings { #[derive(Debug)]
pub fn to_string_no_db(&self) -> Secret<String> { pub enum Environment {
Secret::new(format!( Local,
"postgres://{}:{}@{}:{}", Production,
self.username,
self.password.expose_secret(),
self.host,
self.port
))
}
pub fn to_string(&self) -> Secret<String> {
Secret::new(format!(
"postgres://{}:{}@{}:{}/{}",
self.username,
self.password.expose_secret(),
self.host,
self.port,
self.database_name
))
}
} }
pub fn get_config() -> Result<Settings, config::ConfigError> { pub fn get_config() -> Result<Settings, config::ConfigError> {
@ -58,14 +51,36 @@ pub fn get_config() -> Result<Settings, config::ConfigError> {
let settings = config::Config::builder() let settings = config::Config::builder()
.add_source(config::File::from(config_directory.join("base.toml"))) .add_source(config::File::from(config_directory.join("base.toml")))
.add_source(config::File::from(config_directory.join(env_filename))) .add_source(config::File::from(config_directory.join(env_filename)))
.add_source(
config::Environment::with_prefix("APP")
.prefix_separator("_")
.separator("__"),
)
.build()?; .build()?;
settings.try_deserialize::<Settings>() settings.try_deserialize::<Settings>()
} }
#[derive(Debug)] impl DatabaseSettings {
pub enum Environment { pub fn without_db(&self) -> PgConnectOptions {
Local, let ssl_mode = if self.require_ssl {
Production, PgSslMode::Require
} else {
PgSslMode::Prefer
};
PgConnectOptions::new()
.host(&self.host)
.username(&self.username)
.password(self.password.expose_secret())
.port(self.port)
.ssl_mode(ssl_mode)
}
pub fn with_db(&self) -> PgConnectOptions {
self.without_db()
.database(&self.database_name)
.log_statements(tracing_log::log::LevelFilter::Trace)
}
} }
impl Display for Environment { impl Display for Environment {

View File

@ -1,4 +1,3 @@
use secrecy::ExposeSecret;
use sqlx::postgres::PgPoolOptions; use sqlx::postgres::PgPoolOptions;
use tokio::net::TcpListener; use tokio::net::TcpListener;
use zero2prod::{ use zero2prod::{
@ -12,9 +11,7 @@ async fn main() -> Result<(), std::io::Error> {
let subscriber = get_subscriber("zero2prod", "info", std::io::stdout); let subscriber = get_subscriber("zero2prod", "info", std::io::stdout);
init_subscriber(subscriber); init_subscriber(subscriber);
let config = get_config().expect("Failed to read configuation."); let config = get_config().expect("Failed to read configuation.");
let pool = PgPoolOptions::new() let pool = PgPoolOptions::new().connect_lazy_with(config.database.with_db());
.connect_lazy(config.database.to_string().expose_secret())
.expect("Failed to create Postgres connection pool.");
let addr = format!("{}:{}", config.application.host, config.application.port); let addr = format!("{}:{}", config.application.host, config.application.port);
let listener = TcpListener::bind(addr) let listener = TcpListener::bind(addr)
.await .await

View File

@ -1,7 +1,6 @@
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use reqwest::Client; use reqwest::Client;
use secrecy::ExposeSecret; use sqlx::{Connection, Executor, PgConnection, PgPool};
use sqlx::{postgres::PgPoolOptions, Connection, Executor, PgConnection, PgPool};
use tokio::net::TcpListener; use tokio::net::TcpListener;
use uuid::Uuid; use uuid::Uuid;
use zero2prod::{ use zero2prod::{
@ -29,7 +28,7 @@ async fn health_check() {
async fn subscribe_returns_200_for_valid_form_data() { async fn subscribe_returns_200_for_valid_form_data() {
let app = spawn_app().await; let app = spawn_app().await;
let config = get_config().expect("Failed to read configuration."); let config = get_config().expect("Failed to read configuration.");
let mut connection = PgConnection::connect(&config.database.to_string().expose_secret()) let mut connection = PgConnection::connect_with(&config.database.with_db())
.await .await
.expect("Failed to connect to Postgres."); .expect("Failed to connect to Postgres.");
let client = Client::new(); let client = Client::new();
@ -120,7 +119,7 @@ async fn spawn_app() -> TestApp {
} }
async fn configure_database(config: &DatabaseSettings) -> PgPool { async fn configure_database(config: &DatabaseSettings) -> PgPool {
let mut connection = PgConnection::connect(&config.to_string_no_db().expose_secret()) let mut connection = PgConnection::connect_with(&config.without_db())
.await .await
.expect("Failed to connect to Postgres."); .expect("Failed to connect to Postgres.");
@ -137,8 +136,7 @@ async fn configure_database(config: &DatabaseSettings) -> PgPool {
.await .await
.expect("Failed to create database."); .expect("Failed to create database.");
let pool = PgPoolOptions::new() let pool = PgPool::connect_with(config.with_db())
.connect(&config.to_string().expose_secret())
.await .await
.expect("Failed to connect to Postgres."); .expect("Failed to connect to Postgres.");