Skip to content

Commit d69b67a

Browse files
committed
chore: fun
1 parent dfdf485 commit d69b67a

11 files changed

+85
-47
lines changed

Cargo.lock

+12
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ rustflags = ["-C", "link-arg=-fuse-ld=/opt/homebrew/opt/llvm/bin/ld64.lld"]
2727
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
2828

2929
[dependencies]
30+
serde-aux = "4"
3031
tracing-actix-web = "0.7"
3132
secrecy = { version = "0.8", features = ["serde"] }
3233
tracing-log = "0.1"

configuration/local.yml

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
application:
2-
host: 127.0.0.1
2+
host: 127.0.0.1
3+
database:
4+
require_ssl: false

configuration/production.yml

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
application:
2-
host: 0.0.0.0
2+
host: 0.0.0.0
3+
database:
4+
require_ssl: true

migrations/20231203144603_create_subscriptions_table.sql

-1
This file was deleted.

migrations/20231203202412_create_subscriptions_table.sql

-1
This file was deleted.

spec.yaml

+24-1
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,27 @@ services:
2525
instance_size_slug: basic-xxs
2626
# All incoming requests should be routed to our app
2727
routes:
28-
- path: /
28+
- path: /
29+
envs:
30+
- key: APP_DATABASE__USERNAME
31+
scope: RUN_TIME
32+
value: ${newsletter.USERNAME}
33+
- key: APP_DATABASE__PASSWORD
34+
scope: RUN_TIME
35+
value: ${newsletter.PASSWORD}
36+
- key: APP_DATABASE__HOST
37+
scope: RUN_TIME
38+
value: ${newsletter.HOSTNAME}
39+
- key: APP_DATABASE__PORT
40+
scope: RUN_TIME
41+
value: ${newsletter.PORT}
42+
- key: APP_DATABASE__DATABASE_NAME
43+
scope: RUN_TIME
44+
value: ${newsletter.DATABASE}
45+
databases:
46+
# PG = Postgres
47+
- engine: PG
48+
# Database name
49+
name: newsletter
50+
# Again, let's keep the bill lean
51+
num_nodes: 1

src/configuration.rs

+27-20
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use secrecy::ExposeSecret;
22
use secrecy::Secret;
3+
use sqlx::postgres::{PgConnectOptions, PgSslMode};
4+
use serde_aux::field_attributes::deserialize_number_from_string;
35

46
#[derive(serde::Deserialize)]
57
pub struct Settings {
@@ -9,6 +11,7 @@ pub struct Settings {
911

1012
#[derive(serde::Deserialize)]
1113
pub struct ApplicationSettings {
14+
#[serde(deserialize_with = "deserialize_number_from_string")]
1215
pub port: u16,
1316
pub host: String,
1417
}
@@ -17,9 +20,11 @@ pub struct ApplicationSettings {
1720
pub struct DatabaseSettings {
1821
pub username: String,
1922
pub password: Secret<String>,
23+
#[serde(deserialize_with = "deserialize_number_from_string")]
2024
pub port: u16,
2125
pub host: String,
2226
pub database_name: String,
27+
pub require_ssl: bool,
2328
}
2429

2530
pub enum Environment {
@@ -51,25 +56,22 @@ impl TryFrom<String> for Environment {
5156
}
5257
}
5358
impl DatabaseSettings {
54-
pub fn connection_string(&self) -> Secret<String> {
55-
Secret::new(format!(
56-
"postgres://{}:{}@{}:{}/{}",
57-
self.username,
58-
self.password.expose_secret(),
59-
self.host,
60-
self.port,
61-
self.database_name
62-
))
59+
pub fn without_db(&self) -> PgConnectOptions {
60+
let ssl_mode = if self.require_ssl {
61+
PgSslMode::Require
62+
} else {
63+
PgSslMode::Prefer
64+
};
65+
PgConnectOptions::new()
66+
.host(&self.host)
67+
.username(&self.username)
68+
.password(self.password.expose_secret())
69+
.port(self.port)
70+
.ssl_mode(ssl_mode)
6371
}
6472

65-
pub fn connection_string_without_db(&self) -> Secret<String> {
66-
Secret::new(format!(
67-
"postgres://{}:{}@{}:{}",
68-
self.username,
69-
self.password.expose_secret(),
70-
self.host,
71-
self.port
72-
))
73+
pub fn with_db(&self) -> PgConnectOptions {
74+
self.without_db().database(&self.database_name)
7375
}
7476
}
7577

@@ -84,12 +86,17 @@ pub fn get_configuration() -> Result<Settings, config::ConfigError> {
8486
.expect("Failed to parse APP_ENVIRONMENT.");
8587
let environment_filename = format!("{}.yml", environment.as_str());
8688
let settings = config::Config::builder()
87-
.add_source(config::File::from(
88-
configuration_directory.join("base.yml"),
89-
))
89+
.add_source(config::File::from(configuration_directory.join("base.yml")))
9090
.add_source(config::File::from(
9191
configuration_directory.join(environment_filename),
9292
))
93+
// Add in settings from environment variables (with a prefix of APP and '__' as separator)
94+
// E.g. `APP_APPLICATION__PORT=5001 would set `Settings.application.port`
95+
.add_source(
96+
config::Environment::with_prefix("APP")
97+
.prefix_separator("_")
98+
.separator("__"),
99+
)
93100
.build()?;
94101
settings.try_deserialize::<Settings>()
95102
}

src/main.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
use rust_learn_api::configuration::get_configuration;
22
use rust_learn_api::startup::run;
33
use rust_learn_api::telemetry::{get_subscriber, init_subscriber};
4-
use secrecy::ExposeSecret;
5-
use sqlx::PgPool;
64
use std::net::TcpListener;
5+
use sqlx::postgres::PgPoolOptions;
76

87
#[tokio::main]
98
async fn main() -> Result<(), std::io::Error> {
109
let subscriber = get_subscriber("rust-learn-api".into(), "info".into(), std::io::stdout);
1110
init_subscriber(subscriber);
1211
let config = get_configuration().expect("Failed to read configuration.");
13-
let db_pool = PgPool::connect_lazy(&config.database.connection_string().expose_secret())
14-
.expect("Failed to connect to Postgres.");
12+
let db_pool = PgPoolOptions::new().connect_lazy_with(config.database.with_db());
1513
let address = format!("{}:{}", config.application.host, config.application.port);
1614
let listener = TcpListener::bind(address)?;
1715
run(listener, db_pool).await?.await

src/routes/subscriptions.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,11 @@ pub async fn insert_subscriber(pool: &PgPool, form: &FormData) -> Result<(), sql
3939
form.name,
4040
Utc::now()
4141
)
42-
.execute(pool)
43-
.await
44-
.map_err(|e| {
45-
tracing::error!("Failed to execute query: {:?}", e);
46-
e
47-
})?;
42+
.execute(pool)
43+
.await
44+
.map_err(|e| {
45+
tracing::error!("Failed to execute query: {:?}", e);
46+
e
47+
})?;
4848
Ok(())
4949
}

tests/health_check.rs

+7-12
Original file line numberDiff line numberDiff line change
@@ -115,21 +115,16 @@ async fn spawn_app() -> TestApp {
115115
}
116116
}
117117

118-
pub async fn configure_database(config: &DatabaseSettings) -> PgPool {
119-
// Create database
120-
let mut connection =
121-
PgConnection::connect(&config.connection_string_without_db().expose_secret())
122-
.await
123-
.expect("Failed to connect to Postgres");
124-
connection
125-
.execute(format!(r#"CREATE DATABASE "{}";"#, config.database_name).as_str())
118+
pub async fn configure_database(config: &DatabaseSettings) -> PgPool { // Create database
119+
let mut connection = PgConnection::connect_with(&config.without_db())
126120
.await
121+
.expect("Failed to connect to Postgres");
122+
connection
123+
.execute(format!(r#"CREATE DATABASE "{}";"#, config.database_name).as_str()) .await
127124
.expect("Failed to create database.");
128125
// Migrate database
129-
let connection_pool = PgPool::connect(&config.connection_string().expose_secret())
130-
.await
131-
.expect("Failed to connect to Postgres.");
132-
sqlx::migrate!("./migrations")
126+
let connection_pool = PgPool::connect_with(config.with_db()) .await
127+
.expect("Failed to connect to Postgres."); sqlx::migrate!("./migrations")
133128
.run(&connection_pool)
134129
.await
135130
.expect("Failed to migrate the database");

0 commit comments

Comments
 (0)