From d62d7d635479c90539a0bfc0afb62bee7a71a2b7 Mon Sep 17 00:00:00 2001 From: Nicholas Chitty Date: Mon, 15 Jan 2024 15:40:15 -0500 Subject: [PATCH 1/9] Impl Default for Recipe --- lambda/src/recipe/mod.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lambda/src/recipe/mod.rs b/lambda/src/recipe/mod.rs index 2955f95..6607025 100644 --- a/lambda/src/recipe/mod.rs +++ b/lambda/src/recipe/mod.rs @@ -6,3 +6,12 @@ pub struct Recipe { id: Uuid, name: String, } + +impl Default for Recipe { + fn default() -> Self { + Self { + id: Uuid::nil(), + name: "Basic Recipe".to_owned(), + } + } +} From a440d5a2d45bbc24f636414f1868220005577d4c Mon Sep 17 00:00:00 2001 From: Nicholas Chitty Date: Mon, 15 Jan 2024 15:49:03 -0500 Subject: [PATCH 2/9] Simple construction --- lambda/src/lib.rs | 1 + lambda/src/main.rs | 5 +++-- lambda/src/services.rs | 15 +++++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 lambda/src/lib.rs create mode 100644 lambda/src/services.rs diff --git a/lambda/src/lib.rs b/lambda/src/lib.rs new file mode 100644 index 0000000..736e46e --- /dev/null +++ b/lambda/src/lib.rs @@ -0,0 +1 @@ +pub mod recipe; diff --git a/lambda/src/main.rs b/lambda/src/main.rs index 934d471..a4763c6 100644 --- a/lambda/src/main.rs +++ b/lambda/src/main.rs @@ -6,7 +6,7 @@ use lambda_http::{run, Error}; use serde::Deserialize; use serde_json::{json, Value}; -mod recipe; +mod services; #[derive(Debug, Deserialize)] struct Root { @@ -32,7 +32,8 @@ async fn main() -> Result<(), Error> { let app = Router::new() .route("/", get(root)) - .route("/ping", get(ping)); + .route("/ping", get(ping)) + .route("/recipes/:id", get(services::recipes::read_one)); run(app).await } diff --git a/lambda/src/services.rs b/lambda/src/services.rs new file mode 100644 index 0000000..2efb076 --- /dev/null +++ b/lambda/src/services.rs @@ -0,0 +1,15 @@ +pub(super) mod recipes { + use axum::extract::{Path}; + use axum::http::StatusCode; + use axum::Json; + use lambda::recipe::Recipe; + use uuid::Uuid; + + + pub(crate) async fn read_one( + Path(_): Path, + ) -> Result, StatusCode> + where { + Ok(Json(Recipe::default())) + } +} From fc0e9e362d0be97868df8c5daeed8b4592116888 Mon Sep 17 00:00:00 2001 From: Nicholas Chitty Date: Mon, 15 Jan 2024 15:49:50 -0500 Subject: [PATCH 3/9] Base repository trait --- lambda/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lambda/src/lib.rs b/lambda/src/lib.rs index 736e46e..bb7856f 100644 --- a/lambda/src/lib.rs +++ b/lambda/src/lib.rs @@ -1 +1,7 @@ +use uuid::Uuid; + pub mod recipe; + +pub trait Repository: Send + Sync { + fn find_by_id(&self, id: Uuid) -> Option; +} From 5ba4a7a59d8b48224df1372ca4ac4683a72384c3 Mon Sep 17 00:00:00 2001 From: Nicholas Chitty Date: Mon, 15 Jan 2024 16:02:21 -0500 Subject: [PATCH 4/9] Code organization + nested --- lambda/src/lib.rs | 1 + lambda/src/main.rs | 4 ++-- lambda/src/services.rs | 15 --------------- lambda/src/services/mod.rs | 8 ++++++++ lambda/src/services/recipes.rs | 10 ++++++++++ 5 files changed, 21 insertions(+), 17 deletions(-) delete mode 100644 lambda/src/services.rs create mode 100644 lambda/src/services/mod.rs create mode 100644 lambda/src/services/recipes.rs diff --git a/lambda/src/lib.rs b/lambda/src/lib.rs index bb7856f..94d8f24 100644 --- a/lambda/src/lib.rs +++ b/lambda/src/lib.rs @@ -1,6 +1,7 @@ use uuid::Uuid; pub mod recipe; +pub mod services; pub trait Repository: Send + Sync { fn find_by_id(&self, id: Uuid) -> Option; diff --git a/lambda/src/main.rs b/lambda/src/main.rs index a4763c6..8b644f6 100644 --- a/lambda/src/main.rs +++ b/lambda/src/main.rs @@ -5,8 +5,8 @@ use axum::Router; use lambda_http::{run, Error}; use serde::Deserialize; use serde_json::{json, Value}; +use lambda::services; -mod services; #[derive(Debug, Deserialize)] struct Root { @@ -33,7 +33,7 @@ async fn main() -> Result<(), Error> { let app = Router::new() .route("/", get(root)) .route("/ping", get(ping)) - .route("/recipes/:id", get(services::recipes::read_one)); + .nest("/recipes", services::recipes()); run(app).await } diff --git a/lambda/src/services.rs b/lambda/src/services.rs deleted file mode 100644 index 2efb076..0000000 --- a/lambda/src/services.rs +++ /dev/null @@ -1,15 +0,0 @@ -pub(super) mod recipes { - use axum::extract::{Path}; - use axum::http::StatusCode; - use axum::Json; - use lambda::recipe::Recipe; - use uuid::Uuid; - - - pub(crate) async fn read_one( - Path(_): Path, - ) -> Result, StatusCode> - where { - Ok(Json(Recipe::default())) - } -} diff --git a/lambda/src/services/mod.rs b/lambda/src/services/mod.rs new file mode 100644 index 0000000..740f133 --- /dev/null +++ b/lambda/src/services/mod.rs @@ -0,0 +1,8 @@ +use axum::Router; +use axum::routing::get; + +mod recipes; +pub fn recipes() -> Router { + Router::new() + .route("/:id", get(recipes::read_one)) +} diff --git a/lambda/src/services/recipes.rs b/lambda/src/services/recipes.rs new file mode 100644 index 0000000..9b04058 --- /dev/null +++ b/lambda/src/services/recipes.rs @@ -0,0 +1,10 @@ +use axum::extract::Path; +use axum::http::StatusCode; +use axum::Json; +use uuid::Uuid; + +use crate::recipe::Recipe; + +pub(super) async fn read_one(Path(_): Path) -> Result, StatusCode> { + Ok(Json(Recipe::default())) +} From e721acc29e93010080dc484d1dafdb6b41420f11 Mon Sep 17 00:00:00 2001 From: Nicholas Chitty Date: Mon, 15 Jan 2024 16:25:15 -0500 Subject: [PATCH 5/9] With context --- lambda/src/services/mod.rs | 14 +++++++++++++- lambda/src/services/recipes.rs | 30 +++++++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/lambda/src/services/mod.rs b/lambda/src/services/mod.rs index 740f133..2df40a0 100644 --- a/lambda/src/services/mod.rs +++ b/lambda/src/services/mod.rs @@ -1,8 +1,20 @@ use axum::Router; use axum::routing::get; +use crate::services::recipes::TestRecipeRepository; mod recipes; + +#[derive(Clone)] +struct ApplicationContext { + pub repo: T, +} + pub fn recipes() -> Router { + let recipe_context = ApplicationContext { + repo: TestRecipeRepository {} + }; + Router::new() - .route("/:id", get(recipes::read_one)) + .route("/:id", get(recipes::read_one::)) + .with_state(recipe_context) } diff --git a/lambda/src/services/recipes.rs b/lambda/src/services/recipes.rs index 9b04058..f8f683b 100644 --- a/lambda/src/services/recipes.rs +++ b/lambda/src/services/recipes.rs @@ -1,10 +1,34 @@ -use axum::extract::Path; +use axum::extract::{Path, State}; use axum::http::StatusCode; use axum::Json; use uuid::Uuid; use crate::recipe::Recipe; +use crate::services::ApplicationContext; +use crate::Repository; -pub(super) async fn read_one(Path(_): Path) -> Result, StatusCode> { - Ok(Json(Recipe::default())) +#[derive(Clone)] +pub(super) struct TestRecipeRepository {} + +impl Repository for TestRecipeRepository { + fn find_by_id(&self, id: Uuid) -> Option { + if id.is_nil() { + return Some(Recipe::default()); + } + None + } +} + +pub(super) async fn read_one( + State(state): State>, + Path(id): Path, +) -> Result, StatusCode> +where + T: Repository, +{ + if let Some(recipe) = state.repo.find_by_id(id) { + return Ok(Json(recipe)); + } + + Err(StatusCode::NOT_FOUND) } From 738d90af15252b8f7b794aaae8c09d660ce3badd Mon Sep 17 00:00:00 2001 From: Nicholas Chitty Date: Mon, 15 Jan 2024 18:11:51 -0500 Subject: [PATCH 6/9] POSTGRESgit ap! LFG --- lambda/Cargo.lock | 4 ++++ lambda/Cargo.toml | 2 +- lambda/src/lib.rs | 2 +- lambda/src/main.rs | 3 ++- lambda/src/recipe/mod.rs | 3 ++- lambda/src/services/mod.rs | 20 ++++++++++++---- lambda/src/services/recipes.rs | 42 +++++++++++++++++++++++++++++----- 7 files changed, 61 insertions(+), 15 deletions(-) diff --git a/lambda/Cargo.lock b/lambda/Cargo.lock index 766ec52..d9957ee 100644 --- a/lambda/Cargo.lock +++ b/lambda/Cargo.lock @@ -1517,6 +1517,7 @@ dependencies = [ "tokio-stream", "tracing", "url", + "uuid", ] [[package]] @@ -1598,6 +1599,7 @@ dependencies = [ "stringprep", "thiserror", "tracing", + "uuid", "whoami", ] @@ -1637,6 +1639,7 @@ dependencies = [ "stringprep", "thiserror", "tracing", + "uuid", "whoami", ] @@ -1661,6 +1664,7 @@ dependencies = [ "tracing", "url", "urlencoding", + "uuid", ] [[package]] diff --git a/lambda/Cargo.toml b/lambda/Cargo.toml index 644e7a8..24a9cf9 100644 --- a/lambda/Cargo.toml +++ b/lambda/Cargo.toml @@ -11,7 +11,7 @@ lambda_http = "0.9.0" lambda_runtime = "0.9.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.111" -sqlx = { version = "0.7.3", features = ["runtime-tokio", "postgres"] } +sqlx = { version = "0.7.3", features = ["runtime-tokio", "macros", "postgres", "uuid"] } tokio = { version = "1", features = ["macros"] } tracing = { version = "0.1", features = ["log"] } tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt"] } diff --git a/lambda/src/lib.rs b/lambda/src/lib.rs index 94d8f24..80993d9 100644 --- a/lambda/src/lib.rs +++ b/lambda/src/lib.rs @@ -4,5 +4,5 @@ pub mod recipe; pub mod services; pub trait Repository: Send + Sync { - fn find_by_id(&self, id: Uuid) -> Option; + async fn find_by_id(&self, id: Uuid) -> Option; } diff --git a/lambda/src/main.rs b/lambda/src/main.rs index 8b644f6..4d3eba4 100644 --- a/lambda/src/main.rs +++ b/lambda/src/main.rs @@ -30,10 +30,11 @@ async fn main() -> Result<(), Error> { .without_time() .init(); + let recipe_service = services::recipes().await; let app = Router::new() .route("/", get(root)) .route("/ping", get(ping)) - .nest("/recipes", services::recipes()); + .nest("/recipes", recipe_service); run(app).await } diff --git a/lambda/src/recipe/mod.rs b/lambda/src/recipe/mod.rs index 6607025..6cf1394 100644 --- a/lambda/src/recipe/mod.rs +++ b/lambda/src/recipe/mod.rs @@ -1,7 +1,8 @@ use serde::{Deserialize, Serialize}; +use sqlx::FromRow; use uuid::Uuid; -#[derive(Deserialize, Serialize)] +#[derive(Deserialize, FromRow, Serialize)] pub struct Recipe { id: Uuid, name: String, diff --git a/lambda/src/services/mod.rs b/lambda/src/services/mod.rs index 2df40a0..e9e6263 100644 --- a/lambda/src/services/mod.rs +++ b/lambda/src/services/mod.rs @@ -1,6 +1,7 @@ -use axum::Router; use axum::routing::get; -use crate::services::recipes::TestRecipeRepository; +use axum::Router; + +use crate::services::recipes::PostgresRecipeRepository; mod recipes; @@ -9,12 +10,21 @@ struct ApplicationContext { pub repo: T, } -pub fn recipes() -> Router { +pub async fn recipes() -> Router { + let db_username = std::env::var("DB_USERNAME").unwrap_or_else(|_| "postgres".to_string()); + let db_password = std::env::var("DB_PASSWORD").unwrap_or_else(|_| "password1234".to_string()); + let db_host = std::env::var("DB_HOST").unwrap_or_else(|_| "db".to_string()); + let db_port = std::env::var("DB_PORT").unwrap_or_else(|_| "5432".to_string()); + let db_name = std::env::var("DB_NAME").unwrap_or_else(|_| "meal-planner".to_string()); + let db_connection_str = format!( + "postgresql://{db_username}:{db_password}@{db_host}:{db_port}/{db_name}" + ); + let recipe_context = ApplicationContext { - repo: TestRecipeRepository {} + repo: PostgresRecipeRepository::new(&db_connection_str).await, }; Router::new() - .route("/:id", get(recipes::read_one::)) + .route("/:id", get(recipes::read_one::)) .with_state(recipe_context) } diff --git a/lambda/src/services/recipes.rs b/lambda/src/services/recipes.rs index f8f683b..d16d347 100644 --- a/lambda/src/services/recipes.rs +++ b/lambda/src/services/recipes.rs @@ -1,6 +1,10 @@ +use std::time::Duration; + use axum::extract::{Path, State}; use axum::http::StatusCode; use axum::Json; +use sqlx::postgres::PgPoolOptions; +use sqlx::{Error, PgPool, Postgres}; use uuid::Uuid; use crate::recipe::Recipe; @@ -10,12 +14,38 @@ use crate::Repository; #[derive(Clone)] pub(super) struct TestRecipeRepository {} -impl Repository for TestRecipeRepository { - fn find_by_id(&self, id: Uuid) -> Option { - if id.is_nil() { - return Some(Recipe::default()); +#[derive(Clone)] +pub(super) struct PostgresRecipeRepository { + pool: PgPool, +} + +impl PostgresRecipeRepository { + pub(super) async fn new(url: &str) -> Self { + let pool: PgPool = PgPoolOptions::new() + .max_connections(5) + .acquire_timeout(Duration::from_secs(3)) + .connect(&url) + .await + .expect("Can\'t connect to database"); + + Self { pool } + } +} + +impl Repository for PostgresRecipeRepository { + async fn find_by_id(&self, id: Uuid) -> Option { + let result: Result = sqlx::query_as("SELECT * FROM recipes WHERE id = $1") + .bind(id) + .fetch_one(&self.pool) + .await; + + match result { + Ok(recipe) => Some(recipe), + Err(err) => { + dbg!(err); + None + }, } - None } } @@ -26,7 +56,7 @@ pub(super) async fn read_one( where T: Repository, { - if let Some(recipe) = state.repo.find_by_id(id) { + if let Some(recipe) = state.repo.find_by_id(id).await { return Ok(Json(recipe)); } From 4a60d8d5275bca51fa4fe0f240909728010bef2d Mon Sep 17 00:00:00 2001 From: Nicholas Chitty Date: Mon, 15 Jan 2024 18:15:06 -0500 Subject: [PATCH 7/9] formatting --- lambda/src/main.rs | 3 +-- lambda/src/services/mod.rs | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lambda/src/main.rs b/lambda/src/main.rs index 4d3eba4..2bd1275 100644 --- a/lambda/src/main.rs +++ b/lambda/src/main.rs @@ -2,11 +2,10 @@ use axum::extract::Query; use axum::response::Json; use axum::routing::get; use axum::Router; +use lambda::services; use lambda_http::{run, Error}; use serde::Deserialize; use serde_json::{json, Value}; -use lambda::services; - #[derive(Debug, Deserialize)] struct Root { diff --git a/lambda/src/services/mod.rs b/lambda/src/services/mod.rs index e9e6263..ea0733f 100644 --- a/lambda/src/services/mod.rs +++ b/lambda/src/services/mod.rs @@ -16,9 +16,8 @@ pub async fn recipes() -> Router { let db_host = std::env::var("DB_HOST").unwrap_or_else(|_| "db".to_string()); let db_port = std::env::var("DB_PORT").unwrap_or_else(|_| "5432".to_string()); let db_name = std::env::var("DB_NAME").unwrap_or_else(|_| "meal-planner".to_string()); - let db_connection_str = format!( - "postgresql://{db_username}:{db_password}@{db_host}:{db_port}/{db_name}" - ); + let db_connection_str = + format!("postgresql://{db_username}:{db_password}@{db_host}:{db_port}/{db_name}"); let recipe_context = ApplicationContext { repo: PostgresRecipeRepository::new(&db_connection_str).await, From 7728227a876a76568ec4f943b05e120569048fb3 Mon Sep 17 00:00:00 2001 From: Nicholas Chitty Date: Mon, 15 Jan 2024 18:18:56 -0500 Subject: [PATCH 8/9] Lints --- lambda/src/lib.rs | 3 ++- lambda/src/services/recipes.rs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lambda/src/lib.rs b/lambda/src/lib.rs index 80993d9..ac0e5b7 100644 --- a/lambda/src/lib.rs +++ b/lambda/src/lib.rs @@ -1,8 +1,9 @@ +use std::future::Future; use uuid::Uuid; pub mod recipe; pub mod services; pub trait Repository: Send + Sync { - async fn find_by_id(&self, id: Uuid) -> Option; + fn find_by_id(&self, id: Uuid) -> impl Future>; } diff --git a/lambda/src/services/recipes.rs b/lambda/src/services/recipes.rs index d16d347..02ca912 100644 --- a/lambda/src/services/recipes.rs +++ b/lambda/src/services/recipes.rs @@ -4,7 +4,7 @@ use axum::extract::{Path, State}; use axum::http::StatusCode; use axum::Json; use sqlx::postgres::PgPoolOptions; -use sqlx::{Error, PgPool, Postgres}; +use sqlx::{Error, PgPool}; use uuid::Uuid; use crate::recipe::Recipe; @@ -20,7 +20,7 @@ pub(super) struct PostgresRecipeRepository { } impl PostgresRecipeRepository { - pub(super) async fn new(url: &str) -> Self { + pub(super) async fn new(url: &String) -> Self { let pool: PgPool = PgPoolOptions::new() .max_connections(5) .acquire_timeout(Duration::from_secs(3)) From 8f842e04990c8d5377b15abe0ebd9f98f7a2757c Mon Sep 17 00:00:00 2001 From: Nicholas Chitty Date: Mon, 15 Jan 2024 18:20:01 -0500 Subject: [PATCH 9/9] Fmt --- lambda/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/lambda/src/lib.rs b/lambda/src/lib.rs index ac0e5b7..f78f6a9 100644 --- a/lambda/src/lib.rs +++ b/lambda/src/lib.rs @@ -1,4 +1,5 @@ use std::future::Future; + use uuid::Uuid; pub mod recipe;