Refactors and TODOs: (#27)

- Use anyhow for setup errors
- Implement comparison agaisnt db in insert test
- Crate updates

Reviewed-on: #27
Co-authored-by: Felipe Contreras Salinas <felipe@bstr.cl>
Co-committed-by: Felipe Contreras Salinas <felipe@bstr.cl>
This commit is contained in:
Felipe 2023-08-04 23:46:34 -04:00 committed by Felipe
parent 7c07ede265
commit 579ed9fe34
Signed by: Ludwig
GPG key ID: 441A26F83D31FAFF
5 changed files with 129 additions and 83 deletions

97
Cargo.lock generated
View file

@ -35,6 +35,12 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56fc6cf8dc8c4158eed8649f9b8b0ea1518eb62b544fe9490d66fa0b349eafe9"
[[package]]
name = "anyhow"
version = "1.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854"
[[package]]
name = "async-trait"
version = "0.1.69"
@ -43,7 +49,7 @@ checksum = "7b2d0f03b3640e3a630367e40c468cb7f309529c708ed1d88597047b0e7c6ef7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.23",
"syn 2.0.28",
]
[[package]]
@ -63,9 +69,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "axum"
version = "0.6.18"
version = "0.6.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8175979259124331c1d7bf6586ee7e0da434155e4b2d48ec2c8386281d8df39"
checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf"
dependencies = [
"async-trait",
"axum-core",
@ -91,6 +97,7 @@ dependencies = [
"tower",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
@ -108,6 +115,7 @@ dependencies = [
"rustversion",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
@ -462,7 +470,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.23",
"syn 2.0.28",
]
[[package]]
@ -658,6 +666,7 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
name = "huellas"
version = "0.2.1"
dependencies = [
"anyhow",
"axum",
"axum-test-helper",
"dotenvy",
@ -1061,7 +1070,7 @@ checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.23",
"syn 2.0.28",
]
[[package]]
@ -1297,7 +1306,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e32ca28af694bc1bbf399c33a516dbdf1c90090b8ab23c2bc24f834aa2247f5f"
dependencies = [
"ring",
"rustls-webpki",
"rustls-webpki 0.100.1",
"sct",
]
@ -1320,6 +1329,16 @@ dependencies = [
"untrusted",
]
[[package]]
name = "rustls-webpki"
version = "0.101.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "513722fd73ad80a71f72b61009ea1b584bcfa1483ca93949c8f290298837fa59"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "rustversion"
version = "1.0.13"
@ -1350,29 +1369,29 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.166"
version = "1.0.181"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d01b7404f9d441d3ad40e6a636a7782c377d2abdbe4fa2440e2edcc2f4f10db8"
checksum = "6d3e73c93c3240c0bda063c239298e633114c69a888c3e37ca8bb33f343e9890"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.166"
version = "1.0.181"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dd83d6dde2b6b2d466e14d9d1acce8816dedee94f735eac6395808b3483c6d6"
checksum = "be02f6cb0cd3a5ec20bbcfbcbd749f57daddb1a0882dc2e46a6c236c90b977ed"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.23",
"syn 2.0.28",
]
[[package]]
name = "serde_json"
version = "1.0.99"
version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3"
checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c"
dependencies = [
"itoa",
"ryu",
@ -1514,9 +1533,9 @@ dependencies = [
[[package]]
name = "sqlx"
version = "0.7.0"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ef53c86d2066e04f0ac6b1364f16d13d82388e2d07f11a5c71782345555761"
checksum = "8e58421b6bc416714d5115a2ca953718f6c621a51b68e4f4922aea5a4391a721"
dependencies = [
"sqlx-core",
"sqlx-macros",
@ -1527,9 +1546,9 @@ dependencies = [
[[package]]
name = "sqlx-core"
version = "0.7.0"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a22fd81e9c1ad53c562edb869ff042b215d4eadefefc4784bacfbfd19835945"
checksum = "dd4cef4251aabbae751a3710927945901ee1d97ee96d757f6880ebb9a79bfd53"
dependencies = [
"ahash",
"atoi",
@ -1570,9 +1589,9 @@ dependencies = [
[[package]]
name = "sqlx-macros"
version = "0.7.0"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00bb7c096a202b8164c175614cbfb79fe0e1e0a3d50e0374526183ef2974e4a2"
checksum = "208e3165167afd7f3881b16c1ef3f2af69fa75980897aac8874a0696516d12c2"
dependencies = [
"proc-macro2",
"quote",
@ -1583,9 +1602,9 @@ dependencies = [
[[package]]
name = "sqlx-macros-core"
version = "0.7.0"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37d644623ab9699014e5b3cb61a040d16caa50fd477008f63f1399ae35498a58"
checksum = "8a4a8336d278c62231d87f24e8a7a74898156e34c1c18942857be2acb29c7dfc"
dependencies = [
"dotenvy",
"either",
@ -1607,9 +1626,9 @@ dependencies = [
[[package]]
name = "sqlx-mysql"
version = "0.7.0"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8264c59b28b6858796acfcedc660aa4c9075cc6e4ec8eb03cdca2a3e725726db"
checksum = "8ca69bf415b93b60b80dc8fda3cb4ef52b2336614d8da2de5456cc942a110482"
dependencies = [
"atoi",
"base64",
@ -1648,9 +1667,9 @@ dependencies = [
[[package]]
name = "sqlx-postgres"
version = "0.7.0"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cab6147b81ca9213a7578f1b4c9d24c449a53953cd2222a7b5d7cd29a5c3139"
checksum = "a0db2df1b8731c3651e204629dd55e52adbae0462fa1bdcbed56a2302c18181e"
dependencies = [
"atoi",
"base64",
@ -1687,9 +1706,9 @@ dependencies = [
[[package]]
name = "sqlx-sqlite"
version = "0.7.0"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59fba60afa64718104b71eec6984f8779d4caffff3b30cde91a75843c7efc126"
checksum = "be4c21bf34c7cae5b283efb3ac1bcc7670df7561124dc2f8bdc0b59be40f79a2"
dependencies = [
"atoi",
"flume",
@ -1736,9 +1755,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.23"
version = "2.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737"
checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567"
dependencies = [
"proc-macro2",
"quote",
@ -1782,7 +1801,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.23",
"syn 2.0.28",
]
[[package]]
@ -1837,7 +1856,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.23",
"syn 2.0.28",
]
[[package]]
@ -1883,9 +1902,9 @@ dependencies = [
[[package]]
name = "tower-http"
version = "0.4.1"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8bd22a874a2d0b70452d5597b12c537331d49060824a95f49f108994f94aa4c"
checksum = "55ae70283aba8d2a8b411c695c437fe25b8b5e44e23e780662002fc72fb47a82"
dependencies = [
"bitflags 2.3.3",
"bytes",
@ -1939,7 +1958,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.23",
"syn 2.0.28",
]
[[package]]
@ -2106,7 +2125,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.23",
"syn 2.0.28",
"wasm-bindgen-shared",
]
@ -2140,7 +2159,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.23",
"syn 2.0.28",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -2176,11 +2195,11 @@ dependencies = [
[[package]]
name = "webpki-roots"
version = "0.23.1"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338"
checksum = "b291546d5d9d1eab74f069c77749f2cb8504a12caa20f0f2de93ddbf6f411888"
dependencies = [
"rustls-webpki",
"rustls-webpki 0.101.2",
]
[[package]]

View file

@ -4,17 +4,26 @@ version = "0.2.1"
edition = "2021"
license = "AGPL-3.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
axum = "0.6.18"
axum = { version = "0.6.20", features = ["tracing"] }
tracing = "0.1.37"
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
tokio = { version = "1.29.1", features = ["macros", "signal", "rt-multi-thread"] }
tokio = { version = "1.29.1", features = [
"macros",
"signal",
"rt-multi-thread",
] }
dotenvy = "0.15.7"
tower-http = { version = "0.4.1", features = ["fs"] }
serde = { version = "1.0.166", features = ["derive"] }
serde_json = "1.0.99"
tower-http = { version = "0.4.3", features = ["fs"] }
serde = { version = "1.0.181", features = ["derive"] }
serde_json = "1.0.104"
futures = "0.3.28"
axum-test-helper = "0.3.0"
sqlx ={ version = "0.7.0", default-features = false, features = ["runtime-tokio", "tls-rustls", "macros", "migrate", "sqlite"] }
sqlx = { version = "0.7.1", default-features = false, features = [
"runtime-tokio",
"tls-rustls",
"macros",
"migrate",
"sqlite",
] }
anyhow = "1.0.72"

View file

@ -1,3 +1,4 @@
use anyhow::{Context, Result};
use axum::Router;
use sqlx::sqlite::SqlitePool;
use std::net::SocketAddr;
@ -8,7 +9,7 @@ mod place;
mod routes;
#[tokio::main]
async fn main() {
async fn main() -> Result<()> {
dotenvy::dotenv().unwrap_or_default();
tracing_subscriber::registry()
.with(
@ -18,15 +19,15 @@ async fn main() {
.with(tracing_subscriber::fmt::layer())
.init();
let db_url = dotenvy::var("DATABASE_URL").expect("DATABASE_URL not defined");
let db_url = dotenvy::var("DATABASE_URL").context("DATABASE_URL not defined")?;
let pool = SqlitePool::connect(&db_url)
.await
.expect("can't connect to database");
.context("Couldn't connect to database")?;
sqlx::migrate!()
.run(&pool)
.await
.expect("couldn't run migrations");
.context("Couldn't run migrations")?;
let app = Router::new()
.nest("/places", routes::places_routes(pool))
@ -39,13 +40,14 @@ async fn main() {
axum::Server::bind(&address)
.serve(app.into_make_service())
.with_graceful_shutdown(shutdown_signal())
.await
.unwrap();
.await?;
Ok(())
}
async fn shutdown_signal() {
tokio::signal::ctrl_c()
.await
.expect("Ctrl-C shutdown signal");
println!("Received shutdown signal");
.expect("failed to listen for ctrl-c");
tracing::debug!("Received shutdown signal");
}

View file

@ -1,6 +1,6 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
#[derive(Debug, Deserialize, PartialEq, Serialize)]
pub struct Place {
pub id: Option<i64>,
pub name: String,

View file

@ -16,9 +16,11 @@ where
}
async fn get_places(State(pool): State<SqlitePool>) -> Result<Json<Vec<Place>>> {
let places = ::sqlx::query!(
"SELECT id, name, address, open_hours, icon, description, url," +
r#"longitude as "longitude: f64", latitude as "latitude: f64" FROM places WHERE active = TRUE"#
let places = sqlx::query!(
r#"SELECT id, name, address, open_hours, icon, description, url,
longitude as "longitude: f64", latitude as "latitude: f64"
FROM places
WHERE active = TRUE"#
)
.fetch(&pool)
.map_ok(|p| Place {
@ -33,7 +35,8 @@ async fn get_places(State(pool): State<SqlitePool>) -> Result<Json<Vec<Place>>>
url: p.url,
})
.try_collect::<Vec<_>>()
.await.map_err(internal_error)?;
.await
.map_err(internal_error)?;
Ok(Json(places))
}
@ -49,16 +52,12 @@ async fn upsert_place(
}
}
struct Id {
id: i64,
}
async fn insert_place(pool: SqlitePool, mut place: Place) -> Result<Json<Place>> {
let i = ::sqlx::query_as!(
Id,
"INSERT INTO places (name, address, open_hours, icon, description, longitude, latitude, url)\
VALUES (?, ?, ?, ?, ?, ?, ?, ?)\
RETURNING id",
let id = sqlx::query_scalar!(
r#"INSERT INTO places
(name, address, open_hours, icon, description, longitude, latitude, url)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
RETURNING id"#,
place.name,
place.address,
place.open_hours,
@ -72,13 +71,16 @@ async fn insert_place(pool: SqlitePool, mut place: Place) -> Result<Json<Place>>
.await
.map_err(internal_error)?;
place.id = Some(i.id);
place.id = Some(id);
Ok(Json(place))
}
async fn update_place(pool: SqlitePool, place: Place) -> Result<Json<Place>> {
let result = ::sqlx::query!(
"UPDATE places SET (name, address, open_hours, icon, description, longitude, latitude, url) = (?, ?, ?, ?, ?, ?, ?, ?) WHERE id = ?",
r#"UPDATE places
SET (name, address, open_hours, icon, description, longitude, latitude, url)
= (?, ?, ?, ?, ?, ?, ?, ?)
WHERE id = ?"#,
place.name,
place.address,
place.open_hours,
@ -122,24 +124,35 @@ pub fn places_routes(pool: SqlitePool) -> Router {
mod tests {
#![cfg(test)]
// ctor crate generates non upper case globals
#![allow(non_upper_case_globals)]
use super::places_routes;
use crate::place::Place;
use crate::routes;
use axum::http::StatusCode;
use axum::Router;
use axum_test_helper::TestClient;
use sqlx::sqlite::SqlitePool;
fn client(pool: SqlitePool) -> TestClient {
let router = Router::new().nest("/places", routes::places_routes(pool.clone()));
fn client(pool: &SqlitePool) -> TestClient {
let router = Router::new().nest("/places", places_routes(pool.clone()));
TestClient::new(router)
}
async fn get_from_db(pool: &SqlitePool, id: i64) -> Place {
sqlx::query_as!(
Place,
r#"SELECT id, name, address, open_hours, icon, description, url,
longitude as "longitude: f64", latitude as "latitude: f64"
FROM places
WHERE active = ?"#,
id
)
.fetch_one(pool)
.await
.expect("Couldn't get from DB")
}
#[sqlx::test]
async fn test_add_place(pool: SqlitePool) {
let client = client(pool);
let client = client(&pool);
let mut place = Place {
id: None,
name: "Sherlock Holmes".to_owned(),
@ -151,7 +164,7 @@ mod tests {
open_hours: "Tu-Su 09:30-18:00".to_owned(),
url: Some("https://www.sherlock-holmes.co.uk/".to_owned()),
};
// Insert hte place
// Insert the place
let res = client.put("/places").json(&place).send().await;
// We should get a success on the request
assert_eq!(res.status(), StatusCode::OK);
@ -161,13 +174,16 @@ mod tests {
// Add the returned ID to the original place
place.id = res_place.id;
// And now they should be equal
assert_eq!(place, res_place)
assert_eq!(place, res_place);
// TODO: actually query the DB to check the place was inserted
// confirm that the places stored in the DB are also the same
let db_place = get_from_db(&pool, place.id.unwrap()).await;
assert_eq!(place, db_place);
}
#[sqlx::test]
async fn test_get_places(pool: SqlitePool) {
let client = client(pool);
let client = client(&pool);
let mut places = vec![
Place {
id: None,
@ -216,7 +232,7 @@ mod tests {
#[sqlx::test]
async fn test_delete(pool: SqlitePool) {
let client = client(pool);
let client = client(&pool);
let mut places = vec![
Place {
id: None,
@ -271,7 +287,7 @@ mod tests {
#[sqlx::test]
async fn test_update(pool: SqlitePool) {
let client = client(pool);
let client = client(&pool);
let mut places = vec![
Place {
id: None,