Compare commits

..

3 Commits

Author SHA1 Message Date
c6edcbd009 Setup create_task endpoint 2024-05-03 13:10:48 -04:00
f94e356f8e Add v1 namespace for versioned api 2024-05-03 12:48:12 -04:00
2074d4b476 Add get_task endpoint 2024-05-03 12:24:46 -04:00
5 changed files with 120 additions and 11 deletions

View File

@ -1,20 +1,16 @@
// SPDX-License-Identifier: GPL-3.0-Only // SPDX-License-Identifier: GPL-3.0-Only
// Copyright (C) 2024 Luke Harding // Copyright (C) 2024 Luke Harding
use actix_web::{get, HttpResponse, Responder, web}; use actix_web::web;
use crate::db; pub mod v1;
pub struct AppState { pub struct AppState {
pub database_url: String, pub database_url: String,
} }
#[get("/get_tasks")] pub fn configure(cfg: &mut web::ServiceConfig) {
pub async fn get_tasks(data: web::Data<AppState>) -> impl Responder { let scope = web::scope("/api").configure(v1::configure);
let tasks = match db::get_tasks(data.database_url.clone()).await {
Ok(tasks) => tasks,
Err(e) => return HttpResponse::InternalServerError().body(e.to_string()),
};
HttpResponse::Ok().json(tasks) cfg.service(scope);
} }

65
server/src/api/v1.rs Normal file
View File

@ -0,0 +1,65 @@
use actix_web::{get, HttpResponse, post, Responder, web};
use chrono::NaiveDateTime;
use serde::Deserialize;
use uuid::Uuid;
use crate::api::AppState;
use crate::db;
pub fn configure(cfg: &mut web::ServiceConfig) {
let scope = web::scope("/v1")
.service(get_task)
.service(get_tasks)
.service(create_task);
cfg.service(scope);
}
#[get("/get_tasks")]
pub async fn get_tasks(data: web::Data<AppState>) -> impl Responder {
let tasks = match db::get_tasks(data.database_url.clone()).await {
Ok(tasks) => tasks,
Err(e) => return HttpResponse::InternalServerError().body(e.to_string()),
};
HttpResponse::Ok().json(tasks)
}
#[get("/get_task/{uuid}")]
pub async fn get_task(data: web::Data<AppState>, path: web::Path<Uuid>) -> impl Responder {
let uuid = path.into_inner();
let task = match db::get_task(data.database_url.clone(), uuid).await {
Ok(task) => task,
Err(e) => return HttpResponse::InternalServerError().body(e.to_string()),
};
HttpResponse::Ok().json(task)
}
#[derive(Deserialize)]
struct JsonTask {
pub title: String,
pub description: String,
pub due_date: NaiveDateTime,
}
#[post("/create_task")]
pub async fn create_task(data: web::Data<AppState>, task: web::Json<JsonTask>) -> impl Responder {
let uuid = Uuid::new_v4();
let new_task = match db::create_task(
data.database_url.clone(),
uuid,
&task.title,
&task.description,
task.due_date,
)
.await
{
Ok(task) => task,
Err(e) => return HttpResponse::InternalServerError().body(e.to_string()),
};
HttpResponse::Ok().json(new_task)
}

View File

@ -3,12 +3,19 @@
use std::error; use std::error;
use diesel::{Connection, PgConnection, QueryDsl, RunQueryDsl, SelectableHelper}; use chrono::NaiveDateTime;
use diesel::{
Connection, ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl, SelectableHelper,
};
use diesel::pg::Pg; use diesel::pg::Pg;
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness}; use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
use uuid::Uuid;
use models::Task; use models::Task;
use crate::db::models::NewTask;
use crate::schema::tasks;
pub mod models; pub mod models;
pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("./migrations"); pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("./migrations");
@ -32,3 +39,33 @@ pub async fn get_tasks(database_url: String) -> Result<Vec<Task>, Box<dyn error:
Ok(tasks.select(Task::as_select()).load(conn)?) Ok(tasks.select(Task::as_select()).load(conn)?)
} }
pub async fn get_task(database_url: String, uuid: Uuid) -> Result<Task, Box<dyn error::Error>> {
use crate::schema::tasks::dsl::*;
let conn = &mut establish_connection(&database_url)?;
Ok(tasks.filter(id.eq(uuid)).first(conn)?)
}
pub async fn create_task(
database_url: String,
uuid: Uuid,
title: &str,
description: &str,
due_date: NaiveDateTime,
) -> Result<Task, Box<dyn error::Error>> {
let conn = &mut establish_connection(&database_url)?;
let new_task = NewTask {
id: uuid,
title,
description,
due_date,
};
Ok(diesel::insert_into(tasks::table)
.values(&new_task)
.returning(Task::as_returning())
.get_result(conn)?)
}

View File

@ -6,6 +6,8 @@ use diesel::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use uuid::Uuid; use uuid::Uuid;
use crate::schema::tasks;
#[derive(Queryable, Selectable, Serialize, Deserialize)] #[derive(Queryable, Selectable, Serialize, Deserialize)]
#[diesel(table_name = crate::schema::tasks)] #[diesel(table_name = crate::schema::tasks)]
#[diesel(check_for_backend(diesel::pg::Pg))] #[diesel(check_for_backend(diesel::pg::Pg))]
@ -16,3 +18,12 @@ pub struct Task {
pub complete: bool, pub complete: bool,
pub due_date: NaiveDateTime, pub due_date: NaiveDateTime,
} }
#[derive(Insertable, Deserialize, Serialize)]
#[diesel(table_name = tasks)]
pub struct NewTask<'a> {
pub id: Uuid,
pub title: &'a str,
pub description: &'a str,
pub due_date: NaiveDateTime,
}

View File

@ -76,7 +76,7 @@ async fn main() -> io::Result<()> {
.app_data(web::Data::new(AppState { .app_data(web::Data::new(AppState {
database_url: database_url.clone(), database_url: database_url.clone(),
})) }))
.service(api::get_tasks) .configure(api::configure)
}) })
.bind((bind_addr, port))? .bind((bind_addr, port))?
.run() .run()