diff --git a/Cargo.lock b/Cargo.lock index d804467..b630e26 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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]] diff --git a/Cargo.toml b/Cargo.toml index cb9bc42..02dbcf8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/main.rs b/src/main.rs index 2ad4d60..10cda6a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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"); } diff --git a/src/place.rs b/src/place.rs index d1e811e..0a06c24 100644 --- a/src/place.rs +++ b/src/place.rs @@ -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, pub name: String, diff --git a/src/routes.rs b/src/routes.rs index 79e242c..377c96d 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -16,9 +16,11 @@ where } async fn get_places(State(pool): State) -> Result>> { - 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) -> Result>> url: p.url, }) .try_collect::>() - .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> { - 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> .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> { 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,