diff --git a/server/src/api/v1.rs b/server/src/api/v1.rs index e9ed242..10d3217 100644 --- a/server/src/api/v1.rs +++ b/server/src/api/v1.rs @@ -1,4 +1,4 @@ -use actix_web::{delete, get, HttpResponse, post, Responder, web}; +use actix_web::{delete, get, HttpResponse, patch, post, Responder, web}; use chrono::NaiveDateTime; use serde::Deserialize; use uuid::Uuid; @@ -11,14 +11,15 @@ pub fn configure(cfg: &mut web::ServiceConfig) { .service(get_task) .service(get_tasks) .service(create_task) - .service(delete_task); + .service(delete_task) + .service(update_task); cfg.service(scope); } #[get("/get_tasks")] pub async fn get_tasks(data: web::Data) -> impl Responder { - let tasks = match db::get_tasks(data.database_url.clone()).await { + let tasks = match db::get_tasks(&data.database_url).await { Ok(tasks) => tasks, Err(e) => return HttpResponse::InternalServerError().body(e.to_string()), }; @@ -30,7 +31,7 @@ pub async fn get_tasks(data: web::Data) -> impl Responder { pub async fn get_task(data: web::Data, path: web::Path) -> impl Responder { let uuid = path.into_inner(); - let task = match db::get_task(data.database_url.clone(), uuid).await { + let task = match db::get_task(&data.database_url, uuid).await { Ok(task) => task, Err(e) => return HttpResponse::InternalServerError().body(e.to_string()), }; @@ -39,25 +40,20 @@ pub async fn get_task(data: web::Data, path: web::Path) -> impl } #[derive(Deserialize)] -struct JsonTask { +pub struct JsonNewTask { pub title: String, pub description: String, pub due_date: NaiveDateTime, } #[post("/create_task")] -pub async fn create_task(data: web::Data, task: web::Json) -> impl Responder { - let uuid = Uuid::now_v7(); +pub async fn create_task( + data: web::Data, + task: web::Json, +) -> impl Responder { + let task = task.into_inner(); - let new_task = match db::create_task( - data.database_url.clone(), - uuid, - &task.title, - &task.description, - task.due_date, - ) - .await - { + let new_task = match db::create_task(&data.database_url, task).await { Ok(task) => task, Err(e) => return HttpResponse::InternalServerError().body(e.to_string()), }; @@ -69,7 +65,7 @@ pub async fn create_task(data: web::Data, task: web::Json) - pub async fn delete_task(data: web::Data, path: web::Path) -> impl Responder { let uuid = path.into_inner(); - let result = match db::delete_task(data.database_url.clone(), uuid).await { + let result = match db::delete_task(&data.database_url, uuid).await { Ok(result) => result, Err(e) => return HttpResponse::InternalServerError().body(e.to_string()), }; @@ -80,3 +76,26 @@ pub async fn delete_task(data: web::Data, path: web::Path) -> im HttpResponse::NotFound().body("Unable to delete. Not found.") } } + +#[derive(Deserialize)] +pub struct JsonPatchTask { + pub title: Option, + pub description: Option, + pub complete: Option, + pub due_date: Option, +} + +#[patch("/update_task/{uuid}")] +pub async fn update_task( + data: web::Data, + path: web::Path, + new_values: web::Json, +) -> impl Responder { + let new_values = new_values.into_inner(); + let uuid = path.into_inner(); + + match db::update_task(&data.database_url, uuid, new_values).await { + Ok(task) => HttpResponse::Ok().json(task), + Err(e) => HttpResponse::InternalServerError().body(e.to_string()), + } +} diff --git a/server/src/db/mod.rs b/server/src/db/mod.rs index cff24e2..21ddafc 100644 --- a/server/src/db/mod.rs +++ b/server/src/db/mod.rs @@ -3,7 +3,6 @@ use std::error; -use chrono::NaiveDateTime; use diesel::{ Connection, ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl, SelectableHelper, }; @@ -13,6 +12,7 @@ use uuid::Uuid; use models::Task; +use crate::api::v1::{JsonNewTask, JsonPatchTask}; use crate::db::models::NewTask; use crate::schema; @@ -32,36 +32,33 @@ pub fn run_migrations( Ok(()) } -pub async fn get_tasks(database_url: String) -> Result, Box> { +pub async fn get_tasks(database_url: &str) -> Result, Box> { use schema::tasks::dsl::*; - let conn = &mut establish_connection(&database_url)?; + let conn = &mut establish_connection(database_url)?; Ok(tasks.select(Task::as_select()).load(conn)?) } -pub async fn get_task(database_url: String, uuid: Uuid) -> Result> { +pub async fn get_task(database_url: &str, uuid: Uuid) -> Result> { use schema::tasks::dsl::*; - let conn = &mut establish_connection(&database_url)?; + let conn = &mut establish_connection(database_url)?; Ok(tasks.find(uuid).select(Task::as_select()).first(conn)?) } pub async fn create_task( - database_url: String, - uuid: Uuid, - title: &str, - description: &str, - due_date: NaiveDateTime, + database_url: &str, + task: JsonNewTask, ) -> Result> { - let conn = &mut establish_connection(&database_url)?; + let conn = &mut establish_connection(database_url)?; let new_task = NewTask { - id: uuid, - title, - description, - due_date, + id: Uuid::now_v7(), + title: &task.title, + description: &task.description, + due_date: task.due_date, }; Ok(diesel::insert_into(schema::tasks::table) @@ -70,10 +67,48 @@ pub async fn create_task( .get_result(conn)?) } -pub async fn delete_task(database_url: String, uuid: Uuid) -> Result> { +pub async fn delete_task(database_url: &str, uuid: Uuid) -> Result> { use schema::tasks::dsl::*; - let conn = &mut establish_connection(&database_url)?; + let conn = &mut establish_connection(database_url)?; Ok(diesel::delete(tasks.filter(id.eq(uuid))).execute(conn)?) } + +pub async fn update_task( + database_url: &str, + uuid: Uuid, + new_values: JsonPatchTask, +) -> Result> { + use schema::tasks::dsl::*; + + let conn = &mut establish_connection(database_url)?; + + let mut current_task = get_task(database_url, uuid).await?; + + if let Some(new_title) = new_values.title { + current_task.title = new_title; + } + + if let Some(new_description) = new_values.description { + current_task.description = new_description; + } + + if let Some(new_complete) = new_values.complete { + current_task.complete = new_complete; + } + + if let Some(new_due_date) = new_values.due_date { + current_task.due_date = new_due_date; + } + + Ok(diesel::update(tasks.find(uuid)) + .set(( + title.eq(current_task.title), + description.eq(current_task.description), + complete.eq(current_task.complete), + due_date.eq(current_task.due_date), + )) + .returning(Task::as_returning()) + .get_result(conn)?) +}