From 182e613deb0916d0477fc1ecdebe011e359e4814 Mon Sep 17 00:00:00 2001 From: Daniel O'Connell Date: Tue, 23 Feb 2021 23:26:32 +0100 Subject: [PATCH] Seperate users --- resources/schema.sql | 6 ++-- src/clj/chicken_master/customers.clj | 20 +++++------ src/clj/chicken_master/db.clj | 12 +++++-- src/clj/chicken_master/handler.clj | 41 +++++++++++---------- src/clj/chicken_master/orders.clj | 54 +++++++++++++++------------- src/clj/chicken_master/products.clj | 14 ++++---- 6 files changed, 81 insertions(+), 66 deletions(-) diff --git a/resources/schema.sql b/resources/schema.sql index 58ff7cf..1eeef60 100644 --- a/resources/schema.sql +++ b/resources/schema.sql @@ -9,22 +9,24 @@ CREATE TABLE users ( CREATE TABLE customers ( id SERIAL, - name VARCHAR(512) UNIQUE, + name VARCHAR(512), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted BOOLEAN, user_id INT, PRIMARY KEY(id), + UNIQUE(name, user_id), CONSTRAINT fk_users FOREIGN KEY(user_id) REFERENCES users(id) ); CREATE TABLE products ( id SERIAL, - name VARCHAR(512) UNIQUE, + name VARCHAR(512), amount NUMERIC, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), deleted BOOLEAN, user_id INT, PRIMARY KEY(id), + UNIQUE(name, user_id), CONSTRAINT fk_users FOREIGN KEY(user_id) REFERENCES users(id) ); diff --git a/src/clj/chicken_master/customers.clj b/src/clj/chicken_master/customers.clj index 0307296..a83a6df 100644 --- a/src/clj/chicken_master/customers.clj +++ b/src/clj/chicken_master/customers.clj @@ -4,17 +4,17 @@ [chicken-master.db :as db] [chicken-master.orders :as orders])) -(defn get-all [] - (->> (sql/query db/db-uri ["select * from customers where deleted is null"]) +(defn get-all [user-id] + (->> (sql/query db/db-uri ["select * from customers where deleted is null AND user_id = ?" user-id]) (map (fn [{:customers/keys [id name]}] {:id id :name name})))) -(defn create! [name] +(defn create! [user-id name] (jdbc/execute! db/db-uri - ["INSERT INTO customers (name) VALUES(?) ON CONFLICT (name) DO UPDATE SET deleted = NULL" - name]) - {:customers (get-all)}) + ["INSERT INTO customers (name, user_id) VALUES(?, ?) ON CONFLICT (name, user_id) DO UPDATE SET deleted = NULL" + name user-id]) + {:customers (get-all user-id)}) -(defn delete! [id] - (sql/update! db/db-uri :customers {:deleted true} {:id id}) - {:orders (orders/get-all) - :customers (get-all)}) +(defn delete! [user-id id] + (sql/update! db/db-uri :customers {:deleted true} {:id id :user_id user-id}) + {:orders (orders/get-all user-id) + :customers (get-all user-id)}) diff --git a/src/clj/chicken_master/db.clj b/src/clj/chicken_master/db.clj index 5aa2103..2a687f4 100644 --- a/src/clj/chicken_master/db.clj +++ b/src/clj/chicken_master/db.clj @@ -15,9 +15,15 @@ ["INSERT INTO users (name, password) VALUES (?, crypt(?, gen_salt('bf')))" name passwd])) (defn valid-user? [name passwd] - (jdbc/execute-one! - db-uri - [" SELECT * FROM users WHERE name = ? AND password = crypt(?, password)" name passwd])) + (:users/id (jdbc/execute-one! + db-uri + ["SELECT * FROM users WHERE name = ? AND password = crypt(?, password)" name passwd]))) + +(defn get-by-id + ([tx user-id table id] (get-by-id tx user-id table id :id)) + ([tx user-id table id id-column] + (jdbc/execute-one! tx [(str "SELECT * from " (name table) " WHERE " (name id-column) " = ? AND user_id = ?") + id user-id]))) (comment (create-user "siloa" "krach") diff --git a/src/clj/chicken_master/handler.clj b/src/clj/chicken_master/handler.clj index 98c83b1..7afd314 100644 --- a/src/clj/chicken_master/handler.clj +++ b/src/clj/chicken_master/handler.clj @@ -16,41 +16,44 @@ {:headers {"Content-Type" "application/edn"} :body resp}) -(defn get-customers [] (as-edn (customers/get-all))) -(defn add-customer [request] (as-edn (some-> request :body :name customers/create!))) -(defn delete-customer [id] (as-edn (customers/delete! (edn/read-string id)))) +(defn get-customers [user-id] (as-edn (customers/get-all user-id))) +(defn add-customer [{:keys [body basic-authentication]}] + (as-edn (some->> body :name (customers/create! basic-authentication)))) +(defn delete-customer [user-id id] (->> id edn/read-string (customers/delete! user-id) as-edn)) -(defn get-products [_] (as-edn (products/get-all))) -(defn save-products [request] (as-edn (some-> request :body products/update!))) +(defn get-products [user-id] (as-edn (products/get-all user-id))) +(defn save-products [{:keys [body basic-authentication]}] + (some->> body (products/update! basic-authentication) as-edn)) -(defn get-orders [params] (as-edn {:orders (orders/get-all)})) +(defn get-orders [user-id] (as-edn {:orders (orders/get-all user-id)})) (defn update-order [request] - (let [id (some-> request :route-params :id (Integer/parseInt)) + (let [user-id (:basic-authentication request) + id (some-> request :route-params :id (Integer/parseInt)) order (-> request :body (update :id #(or % id)))] - (as-edn (orders/replace! order)))) + (as-edn (orders/replace! user-id order)))) -(defn delete-order [id] (as-edn (orders/delete! (edn/read-string id)))) -(defn set-order-state [id status] (as-edn (orders/change-state! (edn/read-string id) status))) +(defn delete-order [user-id id] (->> id edn/read-string (orders/delete! user-id) as-edn)) +(defn set-order-state [user-id id status] (as-edn (orders/change-state! user-id (edn/read-string id) status))) -(defn get-stock [params] +(defn get-stock [user-id] (as-edn - {:customers (:body (get-customers)) - :products (:body (get-products params))})) + {:customers (:body (get-customers user-id)) + :products (:body (get-products user-id))})) (defroutes routes - (GET "/stock" {params :query-params} (get-stock params)) - (GET "/customers" [] (get-customers)) + (GET "/stock" [:as {user-id :basic-authentication}] (get-stock user-id)) + (GET "/customers" [:as {user-id :basic-authentication}] (get-customers user-id)) (POST "/customers" request (add-customer request)) - (DELETE "/customers/:id" [id] (delete-customer id)) + (DELETE "/customers/:id" [id :as {user-id :basic-authentication}] (delete-customer user-id id)) (GET "/products" request (get-products request)) (POST "/products" request (save-products request)) - (GET "/orders" {params :query-params} (get-orders params)) + (GET "/orders" [:as {user-id :basic-authentication}] (get-orders user-id)) (POST "/orders" request (update-order request)) (PUT "/orders/:id" request (update-order request)) - (DELETE "/orders/:id" [id] (delete-order id)) - (POST "/orders/:id/:status" [id status] (set-order-state id status)) + (DELETE "/orders/:id" [id :as {user-id :basic-authentication}] (delete-order user-id id)) + (POST "/orders/:id/:status" [id status :as {user-id :basic-authentication}] (set-order-state user-id id status)) (GET "/" [] (resource-response "index.html" {:root "public"})) (resources "/") diff --git a/src/clj/chicken_master/orders.clj b/src/clj/chicken_master/orders.clj index 6a78db7..c59f255 100644 --- a/src/clj/chicken_master/orders.clj +++ b/src/clj/chicken_master/orders.clj @@ -6,14 +6,15 @@ [chicken-master.products :as products] [chicken-master.time :as t])) -(defn- upsert-order! [tx user-id {:keys [id day state notes]}] - (let [order {:customer_id user-id +(defn- upsert-order! [tx user-id customer-id {:keys [id day state notes]}] + (let [order {:customer_id customer-id :notes notes :status (some-> state name jdbc.types/as-other) :order_date (some-> day t/parse-date t/inst->timestamp)}] - (if id + (if (db/get-by-id tx user-id :orders id) (do (sql/update! tx :orders order {:id id}) id) - (:orders/id (sql/insert! tx :orders order))))) + (:orders/id (sql/insert! tx :orders (assoc order :user_id user-id)))))) + (defn- structure-order [items] {:id (-> items first :orders/id) @@ -39,32 +40,35 @@ vals (map structure-order))) -(defn get-order [tx id] - (first (get-orders tx "WHERE o.id = ?" [id]))) +(defn get-order [tx user-id id] + (first (get-orders tx "WHERE o.id = ? AND o.user_id = ?" [id user-id]))) -(defn get-all [] (get-orders db/db-uri nil [])) +(defn get-all [user-id] (get-orders db/db-uri "WHERE o.user_id = ?" [user-id])) -(defn- orders-for-days [tx & days] +(defn- orders-for-days [tx user-id & days] (let [days (remove nil? days)] (->> days (map t/inst->timestamp) (map jdbc.types/as-date) - (get-orders tx (str "WHERE o.order_date::date IN " (db/psql-list days)))))) + (into [user-id]) + (get-orders tx (str "WHERE o.user_id = ? AND o.order_date::date IN " (db/psql-list days)))))) -(defn- orders-between [tx from to] +(defn- orders-between [tx user-id from to] (get-orders tx - "WHERE o.order_date::date >= ? AND o.order_date::date <= ?" + "WHERE o.order_date::date >= ? AND o.order_date::date <= ? AND o.user_id = ?" [(some-> from t/inst->timestamp jdbc.types/as-date) - (some-> to t/inst->timestamp jdbc.types/as-date)])) + (some-> to t/inst->timestamp jdbc.types/as-date) + user-id])) -(defn replace! [{:keys [who products] :as order}] +(defn replace! [user-id {:keys [who products] :as order}] + (prn order) (jdbc/with-transaction [tx db/db-uri] - (let [user-id (or (:id who) - (:customers/id (sql/get-by-id tx :customers (:name who) :name {}))) + (let [customer-id (or (:id who) + (:customers/id (db/get-by-id tx user-id :customers (:name who) :name))) products-map (products/products-map tx products) - previous-day (some->> order :id (sql/get-by-id tx :orders) :orders/order_date (.toInstant)) - order-id (upsert-order! tx user-id order)] + previous-day (some->> order :id (db/get-by-id tx user-id :orders) :orders/order_date (.toInstant)) + order-id (upsert-order! tx user-id customer-id order)] (sql/delete! tx :order_products {:order_id order-id}) (sql/insert-multi! tx :order_products [:order_id :product_id :amount] @@ -72,21 +76,21 @@ :let [product-id (-> n name products-map)] :when product-id] [order-id product-id amount])) - (orders-for-days tx previous-day (some-> order :day t/parse-date))))) + (orders-for-days tx user-id previous-day (some-> order :day t/parse-date))))) -(defn delete! [id] +(defn delete! [user-id id] (jdbc/with-transaction [tx db/db-uri] - (let [day (some->> id (sql/get-by-id tx :orders) :orders/order_date (.toInstant))] - (sql/delete! tx :orders {:id id}) - (when day (orders-for-days tx day))))) + (let [day (some->> id (db/get-by-id tx user-id :orders) :orders/order_date (.toInstant))] + (sql/delete! tx :orders {:id id :user_id user-id}) + (when day (orders-for-days tx user-id day))))) (defn change-state! "Update the state of the given order and also modify the number of products available: * when `fulfilled` decrement the number of products * when `waiting` increment the number (as this means a previously fulfilled order has been returned)" - [id state] + [user-id id state] (jdbc/with-transaction [tx db/db-uri] - (let [order (get-order tx id) + (let [order (get-order tx user-id id) operator (condp = state "fulfilled" "-" "waiting" "+")] @@ -96,4 +100,4 @@ [(str "UPDATE products SET amount = amount " operator " ? WHERE name = ?") amount (name prod)])) (sql/update! tx :orders {:status (jdbc.types/as-other state)} {:id id})) - (orders-for-days tx (-> order :day t/parse-date))))) + (orders-for-days tx user-id (-> order :day t/parse-date))))) diff --git a/src/clj/chicken_master/products.clj b/src/clj/chicken_master/products.clj index 409ee63..412029d 100644 --- a/src/clj/chicken_master/products.clj +++ b/src/clj/chicken_master/products.clj @@ -3,8 +3,8 @@ [next.jdbc.sql :as sql] [chicken-master.db :as db])) -(defn get-all [] - (->> (sql/query db/db-uri ["select * from products where deleted is null"]) +(defn get-all [user-id] + (->> (sql/query db/db-uri ["select * from products where deleted is null and user_id = ?" user-id]) (map (fn [{:products/keys [name amount]}] [(keyword name) amount])) (into {}))) @@ -15,15 +15,15 @@ (map #(vector (:products/name %) (:products/id %))) (into {}))) -(defn update! [new-products] +(defn update! [user-id new-products] (jdbc/with-transaction [tx db/db-uri] (doseq [[prod amount] new-products] (jdbc/execute! tx - ["INSERT INTO products (name, amount) VALUES(?, ?) - ON CONFLICT (name) DO UPDATE SET amount = EXCLUDED.amount, deleted = NULL" - (name prod) amount])) + ["INSERT INTO products (name, amount, user_id) VALUES(?, ?, ?) + ON CONFLICT (name, user_id) DO UPDATE SET amount = EXCLUDED.amount, deleted = NULL" + (name prod) amount user-id])) (sql/update! tx :products {:deleted true} (into [(str "name NOT IN " (db/psql-list (keys new-products)))] (->> new-products keys (map name))))) - (get-all)) + (get-all user-id))