Skip to content

Commit

Permalink
Feature: Implement Item API (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
JijoBose committed Jan 7, 2024
1 parent 08d725c commit dd29d83
Show file tree
Hide file tree
Showing 13 changed files with 186 additions and 1 deletion.
5 changes: 5 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ edition = "2021"

[dependencies]
actix-web = "4.4.0"
diesel = { version = "2.1.4", features = ["postgres", "r2d2"] }
chrono = "0.4.31"
diesel = { version = "2.1.4", features = ["postgres", "r2d2", "chrono", "uuid"] }
diesel_migrations = "2.1.0"
dotenvy = "0.15.7"
dotenv = "0.15.0"
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ Run Migration

``diesel migration run``

Undo Migration

``diesel migration redo``

Run Cargo

``cargo run``
2 changes: 2 additions & 0 deletions migrations/2024-01-07-051817_create_items/down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- This file should undo anything in `up.sql`
DROP table items
11 changes: 11 additions & 0 deletions migrations/2024-01-07-051817_create_items/up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-- Your SQL goes here
CREATE TABLE items (
id VARCHAR NOT NULL PRIMARY KEY,
room_id VARCHAR NOT NULL REFERENCES rooms(id) on DELETE CASCADE,
name VARCHAR NOT NULL,
description VARCHAR,
category VARCHAR NOT NULL,
purchase_date Timestamp NOT NULL,
expiry_date Timestamp,
value DOUBLE PRECISION NOT NULL
)
53 changes: 53 additions & 0 deletions src/app/actions/item.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use actix_web::web::Json;
use diesel::prelude::*;
use uuid::Uuid;

use crate::app::models::item;

type DbError = Box<dyn std::error::Error + Send + Sync>;

pub fn list_items(conn: &mut PgConnection, uid: Uuid) -> Result<Vec<item::Item>, DbError> {
use crate::schema::items::dsl::*;

let item_list = items.filter(room_id.eq(uid.to_string())).load(conn)?;

Ok(item_list)
}

pub fn insert_new_item(
conn: &mut PgConnection,
form: &Json<item::NewItem>,
) -> Result<item::Item, DbError> {
use crate::schema::items::dsl::*;

match form.validate() {
Ok(_) => {
let new_item = item::Item {
id: Uuid::new_v4().to_string(),
room_id: form.room_id.to_owned(),
name: form.name.to_owned(),
description: form.description.to_owned(),
category: form.category.to_owned(),
purchase_date: form.purchase_date.to_owned(),
expiry_date: form.expiry_date.to_owned(),
value: form.value.to_owned(),
};

diesel::insert_into(items).values(&new_item).execute(conn)?;

Ok(new_item)
}
Err(error) => Err(DbError::from(error)),
}
}

pub fn delete_item(conn: &mut PgConnection, uid: Uuid) -> Result<String, DbError> {
use crate::schema::items::dsl::*;

let result = diesel::delete(items.filter(id.eq(uid.to_string()))).execute(conn);

match result {
Ok(_) => Ok("Success".to_string()),
Err(e) => Err(DbError::from(e)),
}
}
1 change: 1 addition & 0 deletions src/app/actions/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod home;
pub mod room;
pub mod item;
39 changes: 39 additions & 0 deletions src/app/api/item.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use actix_web::{error, get, post, web, HttpResponse, Responder, Result};
use diesel::{r2d2, PgConnection};

use crate::app::actions;
use crate::app::models;

type DbPool = r2d2::Pool<r2d2::ConnectionManager<PgConnection>>;

#[post("/item")]
async fn add_item(
pool: web::Data<DbPool>,
form: web::Json<models::item::NewItem>,
) -> Result<impl Responder> {
let response = web::block(move || {
let mut conn = pool.get()?;
actions::item::insert_new_item(&mut conn, &form)
})
.await?
.map_err(error::ErrorBadRequest)?;

Ok(HttpResponse::Created().json(response))
}

#[get("/item")]
async fn get_items(
pool: web::Data<DbPool>,
query: web::Query<models::item::ItemQuery>,
) -> Result<impl Responder> {
let room_uid = query.room_id;

let response = web::block(move || {
let mut conn = pool.get()?;
actions::item::list_items(&mut conn, room_uid)
})
.await?
.map_err(error::ErrorBadRequest)?;

Ok(HttpResponse::Ok().json(response))
}
1 change: 1 addition & 0 deletions src/app/api/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod home;
pub mod room;
pub mod item;
1 change: 1 addition & 0 deletions src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ pub mod db;
pub mod models {
pub mod home;
pub mod room;
pub mod item;
}
49 changes: 49 additions & 0 deletions src/app/models/item.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use diesel::prelude::*;
use diesel::{prelude::Insertable, Queryable};
use serde::{Deserialize, Serialize};
use uuid::Uuid;

use crate::schema::items;
use crate::app::models::room::Room;

/// Item details.
#[derive(Queryable, Serialize, Selectable, Identifiable, Associations, Debug, PartialEq, Insertable)]
#[diesel(belongs_to(Room))]
#[diesel(table_name = items)]
pub struct Item {
pub id: String,
pub name: String,
pub room_id: String,
pub description: Option<String>,
pub category: String,
pub purchase_date: String,
pub expiry_date: Option<String>,
pub value: f64
}

/// New Item.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NewItem {
pub name: String,
pub room_id: String,
pub description: Option<String>,
pub category: String,
pub purchase_date: String,
pub expiry_date: Option<String>,
pub value: f64
}

#[derive(Deserialize)]
pub struct ItemQuery {
pub room_id: Uuid,
}

// validations
impl NewItem {
pub fn validate(&self) -> Result<(), String> {
if self.name.trim().is_empty() {
return Err("Name is empty".to_string());
}
Ok(())
}
}
3 changes: 3 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use app::api::home::{
};

use app::api::room::{add_room, get_room};
use app::api::item::{add_item, get_items};

use app::db::{
initialize_db_pool,
Expand Down Expand Up @@ -41,6 +42,8 @@ async fn main() -> std::io::Result<()> {
.service(delete_home)
.service(add_room)
.service(get_room)
.service(get_items)
.service(add_item)
})
.bind(("127.0.0.1", 8080))?
.run()
Expand Down
15 changes: 15 additions & 0 deletions src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,19 @@ diesel::table! {
}
}

diesel::table! {
items (id) {
id -> Varchar,
room_id -> Varchar,
name -> Varchar,
description -> Nullable<Varchar>,
category -> Varchar,
purchase_date -> Varchar,
expiry_date -> Nullable<Varchar>,
value -> Float8,
}
}

diesel::table! {
rooms (id) {
id -> Varchar,
Expand All @@ -16,9 +29,11 @@ diesel::table! {
}
}

diesel::joinable!(items -> rooms (room_id));
diesel::joinable!(rooms -> homes (home_id));

diesel::allow_tables_to_appear_in_same_query!(
homes,
items,
rooms,
);

0 comments on commit dd29d83

Please sign in to comment.