From 3fc73fa6ac661b58ba99ae95f61d645686b5e567 Mon Sep 17 00:00:00 2001 From: Tobias Eidelpes Date: Wed, 2 Nov 2022 09:40:49 +0100 Subject: [PATCH] Initial commit --- .env | 1 + .gitignore | 11 ++++++++ Cargo.toml | 17 +++++++++++++ migrations/down.sql | 5 ++++ migrations/up.sql | 56 +++++++++++++++++++++++++++++++++++++++++ src/db.rs | 61 +++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 43 ++++++++++++++++++++++++++++++++ 7 files changed, 194 insertions(+) create mode 100644 .env create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 migrations/down.sql create mode 100644 migrations/up.sql create mode 100644 src/db.rs create mode 100644 src/main.rs diff --git a/.env b/.env new file mode 100644 index 0000000..bc90eef --- /dev/null +++ b/.env @@ -0,0 +1 @@ +DATABASE_URL=/tmp/pos.sqlite diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c7430d9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +/target +/Cargo.lock + +*~ +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +auto-save-list +tramp +.\#* diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..4a777e9 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "pos" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tide = "0.16.0" +async-std = { version = "1.12.0", features = ["attributes"] } +sqlx = { version = "0.6.2", features = ["runtime-async-std-native-tls", "chrono", "json", "sqlite"] } +dotenv = "0.15.0" +chrono = "0.4" +serde_json = "1.0" +anyhow = "1.0.65" +serde = { version = "1.0.145", features = ["derive"] } +pretty_env_logger = "0.4.0" diff --git a/migrations/down.sql b/migrations/down.sql new file mode 100644 index 0000000..8a89277 --- /dev/null +++ b/migrations/down.sql @@ -0,0 +1,5 @@ +-- This file should undo anything in `up.sql` +DROP TABLE products; +DROP TABLE orders_products; +DROP TABLE orders; +DROP VIEW totals_per_owner_and_order; diff --git a/migrations/up.sql b/migrations/up.sql new file mode 100644 index 0000000..31be78a --- /dev/null +++ b/migrations/up.sql @@ -0,0 +1,56 @@ +-- Your SQL goes here +CREATE TABLE products ( + id INTEGER PRIMARY KEY NOT NULL, + name TEXT UNIQUE NOT NULL, + price REAL NOT NULL, + owner TEXT NOT NULL, + created_at TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL, + updated_at TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +CREATE TABLE orders ( + id INTEGER PRIMARY KEY NOT NULL, + created_at TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL, + updated_at TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL +); + +CREATE TABLE orders_products ( + order_id INTEGER NOT NULL, + product_id INTEGER NOT NULL, + quantity INTEGER NOT NULL, + FOREIGN KEY (order_id) REFERENCES orders(id), + FOREIGN KEY (product_id) REFERENCES products(id), + PRIMARY KEY (order_id, product_id) +); + +INSERT INTO products (name, price, owner) +VALUES + ("Pfadiburger", 5.1, "Pfadfinder"), + ("Kuchen", 2, "Sportunion"), + ("Apfelsaft", 1.5, "Sportunion"), + ("Käsekrainer", 4, "Pfadfinder"), + ("Süßes", 0.2, "Pfadfinder"), + ("Capuccino", 3.5, "Deluke"), + ("Espresso", 2.5, "Deluke"); + +INSERT INTO orders DEFAULT VALUES; +INSERT INTO orders_products (order_id, product_id, quantity) +VALUES + (1, 1, 3), + (1, 2, 1), + (1, 5, 1); + +INSERT INTO orders DEFAULT VALUES; +INSERT INTO orders_products (order_id, product_id, quantity) +VALUES + (2, 4, 2), + (2, 6, 2), + (2, 2, 1); + +CREATE VIEW totals_per_owner_and_order AS +SELECT id, owner, SUM(total_price) AS total FROM ( + SELECT o.id, p.owner, p.price*op.quantity AS total_price FROM orders AS o + INNER JOIN orders_products AS op ON o.id = op.order_id + INNER JOIN products AS p ON p.id = op.product_id + ) +GROUP BY id, owner; diff --git a/src/db.rs b/src/db.rs new file mode 100644 index 0000000..86bfcd8 --- /dev/null +++ b/src/db.rs @@ -0,0 +1,61 @@ +pub mod models; +pub mod schema; + +use anyhow::Result; +use diesel::prelude::*; +use dotenv::dotenv; +use std::env; + +use self::{models::*, schema::*}; + +pub fn establish_connection() -> SqliteConnection { + dotenv().ok(); + + let db_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set"); + SqliteConnection::establish(&db_url) + .unwrap_or_else(|_| panic!("Error connecting to {}", db_url)) +} + +pub fn get_products() -> Vec { + use self::schema::products::dsl::*; + let conn = &mut establish_connection(); + products + .load::(conn) + .expect("Error loading products") +} + +pub fn get_products_from_order(order: &Order, conn: &mut SqliteConnection) -> Vec { + let res: Vec<(OrderProduct, Product)> = OrderProduct::belonging_to(order) + .inner_join(products::table) + .get_results(conn); + // Strip off all the elements from the linking table + res.into_iter().map(|(_ri, i)| i).collect() +} + +pub fn create_product( + conn: &mut SqliteConnection, + name: &str, + price: &f32, + owner: &str, +) -> Product { + let new_product = NewProduct { name, price, owner }; + + diesel::insert_into(products::table) + .values(&new_product) + .get_result(conn) + .expect("Error saving new product") +} + +pub fn create_order( + conn: &mut SqliteConnection, + products: Vec, +) -> Result<(Order, Vec)> { + let order = diesel::insert_into(orders::table) + .default_values() + .get_result(conn)?; + + for product in products { + let product_exists = diesel::select(diesel::dsl::exists(products.filter(name.eq(product.name)))) + .get_result(conn); + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..8a28038 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,43 @@ +use serde::{Deserialize, Serialize}; +use dotenv; +use sqlx::{sqlite::SqlitePoolOptions, SqlitePool}; +use tide::{Body, Request, Result}; + +#[derive(Deserialize, Serialize)] +struct Product { + name: String, +} + +#[async_std::main] +async fn main() -> Result<()> { + dotenv::dotenv().ok(); + pretty_env_logger::init(); + + let db_pool = make_db_pool().await; + let mut app = tide::new(); + app.at("/products").post(create_product); + app.listen("127.0.0.1:8080").await?; + Ok(()) +} + +pub async fn make_db_pool() -> SqlitePool { + let db_url = + std::env::var("DATABASE_URL").expect("Environment variable DATABASE_URL must be set"); + let db_pool = SqlitePoolOptions::new() + .max_connections(2) + .connect(&format!("sqlite://{}?mode=rwc", &db_url)) + .await + .expect("Could not connect to database"); + db_pool +} + +async fn create_product(mut req: Request<()>) -> Result { + let product: Product = req.body_json().await?; + println!("product name: {}", product.name); + + let product = Product { + name: "chashu".into(), + }; + + Body::from_json(&product) +}