Lootaherra

This commit is contained in:
Jani Pulkkinen
2025-05-29 20:33:15 +03:00
parent b30ddcc402
commit 0756da8b7b
8 changed files with 207 additions and 8 deletions

View File

@@ -1,4 +1,5 @@
-- This file should undo anything in `up.sql` -- This file should undo anything in `up.sql`
drop table loota_admin;
drop table loota_customer; drop table loota_customer;
drop table loota_order; drop table loota_order;
drop table loota_box; drop table loota_box;

View File

@@ -1,4 +1,11 @@
-- Lootakalenteri migration tables -- Lootakalenteri migration tables
create table loota_admin
(
id uuid primary key not null default gen_random_uuid(),
identifier varchar not null,
created_time timestamp with time zone not null default now()
);
create table loota_customer create table loota_customer
( (
id uuid primary key not null default gen_random_uuid(), id uuid primary key not null default gen_random_uuid(),

View File

@@ -1,2 +1,3 @@
-- This file should undo anything in `up.sql` -- This file should undo anything in `up.sql`
delete from loota_admin;
delete from loota_customer; delete from loota_customer;

View File

@@ -1,4 +1,5 @@
-- Test data -- Test data
insert into loota_admin (identifier) values ( 'admin1');
insert into loota_customer (identifier) values ( 'customer1'); insert into loota_customer (identifier) values ( 'customer1');
insert into loota_customer (identifier) values ( 'customer2'); insert into loota_customer (identifier) values ( 'customer2');

View File

@@ -5,11 +5,13 @@ use diesel::result::Error;
use uuid::Uuid; use uuid::Uuid;
use crate::constants::APPLICATION_JSON; use crate::constants::APPLICATION_JSON;
use crate::connection::establish_connection; use crate::connection::establish_connection;
use crate::models::{Customer, Box, Order, LootaResponse, LootaBoxResponse, LootaRequest}; use crate::models::{Customer, Box, Order, LootaResponse, LootaBoxResponse, LootaRequest, LootaOrderAdminResponse, LootaUserAdminResponse, LootaBoxAdminResponse, LootaBoxOrderAdminResponse};
use crate::schema::loota_admin::dsl::loota_admin;
use crate::schema::loota_box::dsl::loota_box; use crate::schema::loota_box::dsl::loota_box;
use crate::schema::loota_box::{delivery_date, id, pickup_date}; use crate::schema::loota_box::{delivery_date, id, pickup_date};
use crate::schema::loota_customer::dsl::loota_customer; use crate::schema::loota_customer::dsl::loota_customer;
use crate::schema::loota_customer::identifier; use crate::schema::loota_customer::identifier;
use crate::schema::loota_order::dsl::loota_order;
fn find_boxes(_identifier: String, _order: &Order, conn: &mut PgConnection) -> LootaResponse { fn find_boxes(_identifier: String, _order: &Order, conn: &mut PgConnection) -> LootaResponse {
let boxes = Box::belonging_to(_order) let boxes = Box::belonging_to(_order)
@@ -45,6 +47,66 @@ fn find_boxes(_identifier: String, _order: &Order, conn: &mut PgConnection) -> L
} }
fn find_admin_boxes(delivery_date_param: String, conn: &mut PgConnection) -> Result<Vec<LootaBoxAdminResponse>, Error> {
let parsed_date = chrono::NaiveDateTime::parse_from_str(&delivery_date_param, "%Y-%m-%dT%H:%M:%S")
.map_err(|_| Error::NotFound)?;
let boxes = loota_box
.filter(delivery_date.ge(Some(parsed_date)))
.inner_join(loota_order)
.select((Box::as_select(), Order::as_select()))
.load::<(Box, Order)>(conn)?;
let response = boxes
.into_iter()
.map(|(box_data, order)| {
LootaBoxAdminResponse {
id: box_data.id.to_string(),
delivery_date: box_data.delivery_date,
pickup_date: box_data.pickup_date,
order: (LootaBoxOrderAdminResponse {
id: order.id.to_string(),
customer_id: order.customer_id.to_string(),
location: order.location,
created_time: order.created_time,
})
}
})
.collect();
Ok(response)
}
fn find_admin_orders(conn: &mut PgConnection) -> Result<Vec<LootaOrderAdminResponse>, Error> {
let orders = loota_order
.inner_join(loota_customer)
.select((Order::as_select(), Customer::as_select()))
.load::<(Order, Customer)>(conn)?;
let response = orders
.into_iter()
.map(|(order, customer)| {
LootaOrderAdminResponse {
id: order.id.to_string(),
location: order.location,
created_time: order.created_time,
user: (LootaUserAdminResponse {
id: customer.id.to_string(),
identifier: customer.identifier,
created_time: customer.created_time,
})
}
})
.collect();
Ok(response)
}
fn find_order(_customer: &Customer, conn: &mut PgConnection) -> Result<LootaResponse, Error> { fn find_order(_customer: &Customer, conn: &mut PgConnection) -> Result<LootaResponse, Error> {
let _identifier = _customer.identifier.clone(); let _identifier = _customer.identifier.clone();
let order = Order::belonging_to(_customer) let order = Order::belonging_to(_customer)
@@ -65,6 +127,17 @@ fn find_order(_customer: &Customer, conn: &mut PgConnection) -> Result<LootaResp
} }
fn find_admin(_identifier: String) -> bool {
let conn = &mut establish_connection();
let admin_exists = loota_admin
.filter(crate::schema::loota_admin::dsl::identifier.eq(&_identifier))
.select(crate::schema::loota_admin::dsl::id)
.first::<Uuid>(conn);
admin_exists.is_ok()
}
fn find_customer(_identifier: String) -> Result<LootaResponse, Error> { fn find_customer(_identifier: String) -> Result<LootaResponse, Error> {
let conn = &mut establish_connection(); let conn = &mut establish_connection();
@@ -119,10 +192,76 @@ fn parse_date(_date: String) -> Result<NaiveDateTime, chrono::ParseError> {
NaiveDateTime::parse_from_str(&_date, "%Y-%m-%dT%H:%M:%S") NaiveDateTime::parse_from_str(&_date, "%Y-%m-%dT%H:%M:%S")
} }
#[get("/lootaherra/{id}")]
async fn get_admin(path: web::Path<String>) -> impl Responder {
let _identifier = path.into_inner();
let exists = web::block(move || find_admin(_identifier)).await.unwrap();
match exists {
true => {
HttpResponse::Ok()
},
false => HttpResponse::NotFound()
}
}
#[get("/lootaherra/orders")]
async fn get_admin_orders() -> impl Responder {
let orders = web::block(move || {
let conn = &mut establish_connection();
find_admin_orders(conn)
})
.await;
match orders {
Ok(Ok(response)) => HttpResponse::Ok()
.content_type(APPLICATION_JSON)
.json(response),
Ok(Err(diesel_error)) => {
println!("Diesel error: {:?}", diesel_error);
HttpResponse::InternalServerError()
.content_type(APPLICATION_JSON)
.body("Database query failed")
}
Err(blocking_error) => {
println!("Blocking error: {:?}", blocking_error);
HttpResponse::InternalServerError()
.content_type(APPLICATION_JSON)
.body("Server thread blocking error")
}
}
}
#[get("/lootaherra/box-list/{delivery_date}")]
async fn get_admin_boxes(path: web::Path<String>) -> impl Responder {
let _delivery_date = path.into_inner();
let boxes = web::block(move || {
let conn = &mut establish_connection();
find_admin_boxes(_delivery_date, conn)
})
.await;
match boxes {
Ok(Ok(response)) => HttpResponse::Ok()
.content_type(APPLICATION_JSON)
.json(response),
_ => HttpResponse::NotFound()
.content_type(APPLICATION_JSON)
.await
.unwrap(),
}
}
#[get("/loota/{id}")] #[get("/loota/{id}")]
async fn get(path: web::Path<String>) -> impl Responder { async fn get(path: web::Path<String>) -> impl Responder {
let identifer = path.into_inner(); let identifer = path.into_inner();
println!("id: {:?}", identifer);
let response = web::block(move || find_customer(identifer)).await.unwrap(); let response = web::block(move || find_customer(identifer)).await.unwrap();

View File

@@ -23,10 +23,11 @@ async fn main() -> io::Result<()> {
HttpServer::new(|| { HttpServer::new(|| {
App::new() App::new()
// enable logger - always register actix-web Logger middleware last
.wrap(middleware::Logger::default()) .wrap(middleware::Logger::default())
// register HTTP requests handlers
.service(loota::get) .service(loota::get)
.service(loota::get_admin_orders)
.service(loota::get_admin)
.service(loota::get_admin_boxes)
.service(loota::update) .service(loota::update)
}) })
.bind("0.0.0.0:9090")? .bind("0.0.0.0:9090")?

View File

@@ -2,23 +2,33 @@ use uuid::Uuid;
use diesel::prelude::*; use diesel::prelude::*;
use chrono::{NaiveDateTime}; use chrono::{NaiveDateTime};
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use crate::schema::{loota_customer, loota_order, loota_box}; use crate::schema::{loota_customer, loota_admin, loota_order, loota_box};
#[derive(Queryable, Identifiable, Selectable, Debug, PartialEq)] #[derive(Queryable, Identifiable, Selectable, Debug, PartialEq)]
#[diesel(table_name = loota_customer)] #[diesel(table_name = loota_customer)]
pub struct Customer { pub struct Customer {
pub id: Uuid, pub id: Uuid,
pub identifier: String pub identifier: String,
pub created_time: NaiveDateTime
} }
#[derive(Queryable, Identifiable, Selectable, Debug, PartialEq)]
#[diesel(table_name = loota_admin)]
pub struct Admin {
pub id: Uuid,
pub identifier: String,
pub created_time: NaiveDateTime
}
#[derive(Queryable, Selectable, Identifiable, Associations, Debug, PartialEq)] #[derive(Queryable, Selectable, Identifiable, Associations, Debug, PartialEq)]
#[diesel(belongs_to(Customer))] #[diesel(belongs_to(Customer, foreign_key = customer_id))]
#[diesel(table_name = loota_order)] #[diesel(table_name = loota_order)]
pub struct Order { pub struct Order {
pub id: Uuid, pub id: Uuid,
pub location: String, pub location: String,
pub customer_id: Uuid, pub customer_id: Uuid,
pub created_time: NaiveDateTime
} }
#[derive(Queryable, Selectable, Identifiable, Associations, Debug, PartialEq)] #[derive(Queryable, Selectable, Identifiable, Associations, Debug, PartialEq)]
@@ -32,7 +42,37 @@ pub struct Box {
pub order_id: Uuid pub order_id: Uuid
} }
#[derive(Debug, Serialize)]
pub struct LootaBoxAdminResponse {
pub id: String,
pub delivery_date: Option<NaiveDateTime>,
pub pickup_date: Option<NaiveDateTime>,
pub order: LootaBoxOrderAdminResponse
}
#[derive(Debug, Serialize)]
pub struct LootaBoxOrderAdminResponse {
pub id: String,
pub customer_id: String,
pub location: String,
pub created_time: NaiveDateTime
}
#[derive(Debug, Serialize)]
pub struct LootaOrderAdminResponse {
pub id: String,
pub location: String,
pub created_time: NaiveDateTime,
pub user: LootaUserAdminResponse
}
#[derive(Debug, Serialize)]
pub struct LootaUserAdminResponse {
pub id: String,
pub identifier: String,
pub created_time: NaiveDateTime
}
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub struct LootaResponse { pub struct LootaResponse {

View File

@@ -1,5 +1,13 @@
// @generated automatically by Diesel CLI. // @generated automatically by Diesel CLI.
diesel::table! {
loota_admin (id) {
id -> Uuid,
identifier -> Varchar,
created_time -> Timestamp,
}
}
diesel::table! { diesel::table! {
loota_box (id) { loota_box (id) {
id -> Uuid, id -> Uuid,
@@ -31,6 +39,7 @@ diesel::joinable!(loota_box -> loota_order (order_id));
diesel::joinable!(loota_order -> loota_customer (customer_id)); diesel::joinable!(loota_order -> loota_customer (customer_id));
diesel::allow_tables_to_appear_in_same_query!( diesel::allow_tables_to_appear_in_same_query!(
loota_admin,
loota_box, loota_box,
loota_customer, loota_customer,
loota_order, loota_order,