This commit is contained in:
Daniel O'Connell 2021-02-23 21:53:08 +01:00
parent c1c695a978
commit d0a14bd1ab
11 changed files with 45 additions and 306 deletions

7
config/dev/config.edn Normal file
View File

@ -0,0 +1,7 @@
{;; Networking settings
:port 3000
:allow-origin ["http://localhost:8280" "http://localhost:3000"]
;; db settings
:db-uri {:jdbcUrl "jdbc:postgresql://localhost/postgres?user=postgres&password=mysecretpassword"}
}

View File

@ -24,7 +24,7 @@
:min-lein-version "2.9.0" :min-lein-version "2.9.0"
:jvm-opts ["-Xmx1G"] :jvm-opts ["-Xmx1G" "-Dconfig=config/dev/config.edn"]
:source-paths ["src/clj" "src/cljs"] :source-paths ["src/clj" "src/cljs"]
@ -84,9 +84,9 @@
:profiles :profiles
{:dev {:dev
{:dependencies [[binaryage/devtools "1.0.2"]] {:dependencies [[binaryage/devtools "1.0.2"]]
:source-paths ["dev"]} :source-paths ["config/dev"]}
:prod {} :prod {:resource-paths ["config/prod"]}
:uberjar {:source-paths ["env/prod/clj"] :uberjar {:source-paths ["env/prod/clj"]
:omit-source true :omit-source true

View File

@ -1,11 +1,9 @@
(ns chicken-master.db (ns chicken-master.db
(:require [clojure.string :as str] (:require [clojure.string :as str]
[next.jdbc :as jdbc] [config.core :refer [env]]
[next.jdbc.types :as jdbc.types] [next.jdbc :as jdbc]))
[next.jdbc.sql :as sql]
[chicken-master.time :as t]))
(def db-uri {:jdbcUrl (or (System/getenv "DB_URI") "jdbc:postgresql://localhost/postgres?user=postgres&password=mysecretpassword")}) (def db-uri (env :db-uri))
(defn psql-list (defn psql-list
([items] (psql-list items "")) ([items] (psql-list items ""))

View File

@ -1,37 +1,41 @@
(ns chicken-master.handler (ns chicken-master.handler
(:require [chicken-master.mocks :as mocks] (:require [chicken-master.db :as db]
[chicken-master.db :as db]
[chicken-master.orders :as orders] [chicken-master.orders :as orders]
[chicken-master.customers :as customers] [chicken-master.customers :as customers]
[chicken-master.products :as products] [chicken-master.products :as products]
[clojure.edn :as edn] [clojure.edn :as edn]
[config.core :refer [env]]
[compojure.core :refer [GET POST PUT DELETE defroutes]] [compojure.core :refer [GET POST PUT DELETE defroutes]]
[compojure.route :refer [resources]] [compojure.route :refer [resources not-found]]
[compojure.handler :refer [api]] [compojure.handler :refer [api]]
[ring.util.response :refer [resource-response]] [ring.util.response :refer [resource-response]]
[ring.middleware.basic-authentication :refer [wrap-basic-authentication]] [ring.middleware.basic-authentication :refer [wrap-basic-authentication]]
[ring.middleware.cors :refer [wrap-cors]])) [ring.middleware.cors :refer [wrap-cors]]))
(defn get-customers [] {:body (customers/get-all)}) (defn as-edn [resp]
(defn add-customer [request] {:body (some-> request :body :name customers/create!)}) {:headers {"Content-Type" "application/edn"}
(defn delete-customer [id] {:body (customers/delete! (edn/read-string id))}) :body resp})
(defn get-products [_] {:body (products/get-all)}) (defn get-customers [] (as-edn (customers/get-all)))
(defn save-products [request] {:body (some-> request :body products/update!)}) (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-orders [params] {:body {:orders (orders/get-all)}}) (defn get-products [_] (as-edn (products/get-all)))
(defn save-products [request] (as-edn (some-> request :body products/update!)))
(defn get-orders [params] (as-edn {:orders (orders/get-all)}))
(defn update-order [request] (defn update-order [request]
(let [id (some-> request :route-params :id (Integer/parseInt)) (let [id (some-> request :route-params :id (Integer/parseInt))
order (-> request :body (update :id #(or % id)))] order (-> request :body (update :id #(or % id)))]
{:body (orders/replace! order)})) (as-edn (orders/replace! order))))
(defn delete-order [id] {:body (orders/delete! (edn/read-string id))}) (defn delete-order [id] (as-edn (orders/delete! (edn/read-string id))))
(defn set-order-state [id status] {:body (orders/change-state! (edn/read-string id) status)}) (defn set-order-state [id status] (as-edn (orders/change-state! (edn/read-string id) status)))
(defn get-stock [params] (defn get-stock [params]
{:body (as-edn
{:customers (:body (get-customers)) {:customers (:body (get-customers))
:products (:body (get-products params))}}) :products (:body (get-products params))}))
(defroutes routes (defroutes routes
(GET "/stock" {params :query-params} (get-stock params)) (GET "/stock" {params :query-params} (get-stock params))
@ -49,15 +53,13 @@
(POST "/orders/:id/:status" [id status] (set-order-state id status)) (POST "/orders/:id/:status" [id status] (set-order-state id status))
(GET "/" [] (resource-response "index.html" {:root "public"})) (GET "/" [] (resource-response "index.html" {:root "public"}))
(resources "/")) (resources "/")
(not-found "not found"))
(defn- handle-edn [response] (defn- handle-edn [response]
(if (-> response :body type #{java.io.File java.lang.String}) (if (= (get-in response [:headers "Content-Type"]) "application/edn")
response (update response :body pr-str)
(-> response response))
(assoc-in [:headers "Content-Type"] "application/edn")
(update :body pr-str))))
(defn wrap-edn-response [handler] (defn wrap-edn-response [handler]
(fn (fn
@ -79,7 +81,7 @@
(def handler (-> routes (def handler (-> routes
(wrap-basic-authentication authenticated?) (wrap-basic-authentication authenticated?)
(wrap-cors :access-control-allow-origin [#"http://localhost:8280"] (wrap-cors :access-control-allow-origin (map re-pattern (env :allow-origin))
:access-control-allow-methods [:get :put :post :delete :options]) :access-control-allow-methods [:get :put :post :delete :options])
api api
wrap-edn-request wrap-edn-request

View File

@ -1,101 +0,0 @@
(ns chicken-master.mocks
(:import [java.time Instant]
[java.time.temporal ChronoUnit]))
(defn format-date [d] (-> d str (subs 0 10)))
(defn days-range [days date ]
(map #(.plus date % ChronoUnit/DAYS) (range days)))
;;;; Stock
(def stock-products (atom {:eggs 22 :milk 32 :cabbage 54 :carrots 11 :cows 32 :ants 21}))
(defn get-all-products [] @stock-products)
(defn save-stocks [new-products] (reset! stock-products new-products))
;;; Orders
(def id-counter (atom -1))
(def notes ["bezglutenowy"
"tylko z robakami"
"przyjdzie wieczorem"
"wisi 2.50"
"chciała ukraść kozę"])
(def products (atom [:eggs :milk :cabbage :carrots]))
(def customers (atom [{:id 1 :name "mr.blobby (649 234 234)"}
{:id 2 :name "da police (0118 999 881 999 119 725 123123 12 3123 123 )"}
{:id 3 :name "johnny"}]))
(def orders
(atom
(->> (-> (Instant/now) (.minus 50 ChronoUnit/DAYS))
(days-range 90)
(map (fn [date]
[(format-date date) (repeatedly (rand-int 6) #(swap! id-counter inc))]))
(map (fn [[day ids]]
(map (fn [i]
{:id i :day day
:notes (when (> (rand) 0.7) (rand-nth notes))
:state :waiting
:who (rand-nth @customers)
:products (->> @products
(random-sample 0.4)
(map #(vector % (rand-int 10)))
(into {}))
}) ids)
))
flatten
(map #(vector (:id %) %))
(into {}))))
(defn fetch-customers [_]
@customers)
(defn fetch-stock [params]
{:customers (fetch-customers params)
:products (get-all-products)})
(defn add-customer [customer-name]
(swap! customers conj {:id (->> @customers (map :id) (apply max) inc)
:name customer-name})
(fetch-stock {}))
(defn delete-customer [id]
{:orders (swap! orders #(->> % (remove (comp #{id} :id :who second)) (into {})))
:customers (swap! customers (partial remove (comp #{id} :id)))})
(defn day-customers [day] [day (->> @orders vals (filter (comp #{day} :day)))])
(defn get-orders [params] @orders)
(defn replace-order [id order]
(prn id)
(prn order)
(println "replacing order" order)
(let [prev-day (:day (@orders (:id order)))
order (update order :id #(or % (swap! id-counter inc)))]
(prn "order 1" order)
(swap! orders assoc (:id order) order)
(prn "order 2" (@orders (:id order)))
(if (or (not prev-day) (= prev-day (:day order)))
{(:day order) (->> order :day day-customers second)}
{prev-day (->> prev-day day-customers second)
(:day order) (->> order :day day-customers second)})))
(defn delete-order [id]
(println "deleting order" id)
(let [day (-> (get @orders id) :day)]
(swap! orders #(dissoc % id))
{day (->> day day-customers second)}))
(defn order-state [id state]
(prn "fulfilling order" id state)
(condp = state
"fulfilled" (->> id (get @orders) :products (swap! stock-products #(merge-with - %1 %2)))
"waiting" (->> id (get @orders) :products (swap! stock-products #(merge-with + %1 %2))))
(let [day (-> (get @orders id) :day)]
(swap! orders #(assoc-in % [id :state] (keyword state)))
(println id (get @orders id))
{day (->> day day-customers second)}))

View File

@ -4,9 +4,6 @@
[chicken-master.db :as db])) [chicken-master.db :as db]))
(defn get-all [] (defn get-all []
(prn "asd" (->> (sql/query db/db-uri ["select * from products where deleted is null"])
(map (fn [{:products/keys [name amount]}] [(keyword name) amount]))
(into {})))
(->> (sql/query db/db-uri ["select * from products where deleted is null"]) (->> (sql/query db/db-uri ["select * from products where deleted is null"])
(map (fn [{:products/keys [name amount]}] [(keyword name) amount])) (map (fn [{:products/keys [name amount]}] [(keyword name) amount]))
(into {}))) (into {})))
@ -19,7 +16,6 @@
(into {}))) (into {})))
(defn update! [new-products] (defn update! [new-products]
(prn new-products)
(jdbc/with-transaction [tx db/db-uri] (jdbc/with-transaction [tx db/db-uri]
(doseq [[prod amount] new-products] (doseq [[prod amount] new-products]
(jdbc/execute! tx (jdbc/execute! tx

View File

@ -9,7 +9,5 @@
(run-jetty handler {:port port :join? false}))) (run-jetty handler {:port port :join? false})))
(comment (comment
(def h (def h (-main))
(let [port (or (env :port) 3000)]
(run-jetty handler {:port port :join? false})))
(.stop h)) (.stop h))

View File

@ -1,128 +0,0 @@
(ns chicken-master.backend-mocks
(:require [chicken-master.time :as time]))
(defn set-item!
"Set `key' in browser's localStorage to `val`."
[key val]
(.setItem (.-localStorage js/window) key val))
(defn get-item
"Returns value of `key' from browser's localStorage."
[key]
(cljs.reader/read-string (.getItem (.-localStorage js/window) key)))
(defn remove-item!
"Remove the browser's localStorage value for the given `key`"
[key]
(.removeItem (.-localStorage js/window) key))
;;;; Stock
(defn get-all-products [] (get-item :stock-products))
(defn save-stocks [new-products]
(set-item! :stock-products new-products))
;;; Orders
(def notes ["bezglutenowy"
"tylko z robakami"
"przyjdzie wieczorem"
"wisi 2.50"
"chciała ukraść kozę"])
(defn storage-swap! [val fun & args]
(set-item! val (apply fun (get-item val) args))
(get-item val))
(defn purge-items []
(doseq [item [:stock-products :products :customers :orders :settings]]
(remove-item! item))
(set-item! :id-counter -1))
(defn generate-items []
(set-item! :settings {})
(set-item! :stock-products {:eggs 22 :milk 32 :cabbage 54 :carrots 11 :cows 32 :ants 21})
(set-item! :id-counter -1)
(set-item! :products [:eggs :milk :cabbage :carrots])
(set-item! :customers [{:id 1 :name "mr.blobby (649 234 234)"}
{:id 2 :name "da police (0118 999 881 999 119 725 123123 12 3123 123 )"}
{:id 3 :name "johnny"}])
(set-item! :orders
(->> (time/date-offset (new js/Date) -50)
(time/days-range 90)
(map (fn [date]
[(time/iso-date date) (repeatedly (rand-int 6) #(storage-swap! :id-counter inc))]))
(map (fn [[day ids]]
(map (fn [i]
{:id i :day day
:notes (when (> (rand) 0.7) (rand-nth notes))
:state :waiting
:who (rand-nth (get-item :customers))
:products (->> (get-item :products)
(random-sample 0.4)
(map #(vector % (rand-int 10)))
(into {}))
}) ids)
))
flatten
(map #(vector (:id %) %))
(into {}))))
(defn fetch-customers [_]
(get-item :customers))
(defn fetch-stock [params]
{:customers (fetch-customers params)
:products (get-all-products)})
(defn add-customer [{:keys [name] :as params}]
(prn name)
(storage-swap! :customers conj {:id (->> (get-item :customers) (map :id) (apply max) inc)
:name name})
(prn (get-item :customers))
(fetch-stock params))
(defn delete-customer [id]
{:orders (storage-swap! :orders #(->> % (remove (comp #{id} :id :who second)) (into {})))
:customers (storage-swap! :customers (partial remove (comp #{id} :id)))})
(defn- day-customers [day] [day (->> :orders get-item vals (filter (comp #{day} :day)))])
(defn- days-between [from to]
(time/days-range
(int (/ (- (time/parse-date to) (time/parse-date from)) (* 24 3600000)))
(time/parse-date from)))
(defn fetch-orders [{:keys [from to]}]
{:orders (get-item :orders)})
(defn- replace-order [id order]
(println "replacing order" order)
(let [prev-day (:day (get (get-item :orders) id))
order (update order :id #(or % (storage-swap! :id-counter inc)))]
(storage-swap! :orders assoc (:id order) order)
(if prev-day
{prev-day (->> prev-day day-customers second)
(:day order) (->> order :day day-customers second)}
{(:day order) (->> order :day day-customers second)})))
(defn- delete-order [id]
(println "deleting order" id (get (get-item :orders) id))
(let [day (-> (get (get-item :orders) id) :day)]
(storage-swap! :orders #(dissoc % id))
{day (->> day day-customers second)}))
(defn- order-state [{id :id state :state :as bla}]
(prn "fulfilling order" id state bla)
(condp = state
:fulfilled (->> id (get (get-item :orders)) :products (storage-swap! :stock-products #(merge-with - %1 %2)))
:waiting (->> id (get (get-item :orders)) :products (storage-swap! :stock-products #(merge-with + %1 %2))))
(let [day (-> (get (get-item :orders) id) :day)]
(storage-swap! :orders #(assoc-in % [id :state] state))
(println id (get (get-item :orders) id))
{day (->> day day-customers second)}))
(comment
(replace-order
{:id 194, :day "2020-11-21", :hour "02:12", :who "mr.blobby (649 234 234)", :products {:eggs 13 :milk 4 :cabbage 7}})
)

View File

@ -1,7 +1,8 @@
(ns chicken-master.config (ns chicken-master.config
(:require [re-frame.core :as re-frame] (:require [re-frame.core :as re-frame]
[chicken-master.time :as time] [chicken-master.time :as time]
[chicken-master.subs :as subs])) [chicken-master.subs :as subs]
[cljs.reader :refer [read-string]]))
(def debug? (def debug?
^boolean goog.DEBUG) ^boolean goog.DEBUG)
@ -14,7 +15,7 @@
(defn get-setting (defn get-setting
"Returns value of `key' from browser's localStorage." "Returns value of `key' from browser's localStorage."
([key] ([key]
(-> js/window (.-localStorage) (.getItem :settings) cljs.reader/read-string (get key))) (-> js/window (.-localStorage) (.getItem :settings) read-string (get key)))
([key default] ([key default]
(if (nil? (get-setting key)) (if (nil? (get-setting key))
default default
@ -32,9 +33,10 @@
:editable-number-inputs (get-setting :editable-number-inputs false) ; only allow number modifications in the edit modal :editable-number-inputs (get-setting :editable-number-inputs false) ; only allow number modifications in the edit modal
:hide-fulfilled-orders (get-setting :hide-fulfilled-orders false) :hide-fulfilled-orders (get-setting :hide-fulfilled-orders false)
:backend-url (get-setting :backend-url "http://localhost:3000/") :backend-url (get-setting :backend-url (.. js/window -location -href)) ; "http://localhost:3000/"
}) })
(defn- settings [key] (defn- settings [key]
(get @(re-frame/subscribe [::subs/settings]) key)) (get @(re-frame/subscribe [::subs/settings]) key))
@ -83,13 +85,4 @@
[:h3 "Ustawienia tyłu"] [:h3 "Ustawienia tyłu"]
(input :backend-url "backend URL" {}) (input :backend-url "backend URL" {})
[:button {:on-click #(re-frame/dispatch
[:chicken-master.events/confirm-action
"na pewno wyczyścić?"
:chicken-master.events/clear-database])} "Wyczyść bazę"]
[:button {:on-click #(re-frame/dispatch
[:chicken-master.events/confirm-action
"na pewno nowe dane wygenerować?"
:chicken-master.events/generate-database])} "Wygeneruj dane"]
]) ])

View File

@ -5,10 +5,7 @@
[chicken-master.time :as time] [chicken-master.time :as time]
[chicken-master.config :refer [settings default-settings]] [chicken-master.config :refer [settings default-settings]]
[day8.re-frame.http-fx] [day8.re-frame.http-fx]
[ajax.edn :as edn] [ajax.edn :as edn]))
;; required for http mocks
[chicken-master.backend-mocks :as mocks]))
(defn http-request [method endpoint & {:keys [params body on-success on-failure] (defn http-request [method endpoint & {:keys [params body on-success on-failure]
:or {on-success ::process-fetched-days :or {on-success ::process-fetched-days
@ -60,9 +57,7 @@
(fn [db [_ response]] (fn [db [_ response]]
(.error js/console (str response)) (.error js/console (str response))
(js/alert "Wystąpił błąd") (js/alert "Wystąpił błąd")
(assoc db :loading false))) (assoc db :loading? false)))
(re-frame/reg-event-fx (re-frame/reg-event-fx
::move-order ::move-order
@ -103,12 +98,6 @@
(re-frame/reg-event-db (re-frame/reg-event-db
::process-fetched-days ::process-fetched-days
(fn [db [_ orders]] (fn [db [_ orders]]
(prn orders)
(prn (:current-days db))
(prn
(let [days (group-by :day orders)]
(for [[day orders] (:current-days db)]
[day (if (contains? days day) (days day) orders)])))
(-> db (-> db
(assoc :loading? nil) (assoc :loading? nil)
(update :current-days (fn [current-days] (update :current-days (fn [current-days]
@ -208,18 +197,3 @@
(re-frame/dispatch-sync [::show-stock]) (re-frame/dispatch-sync [::show-stock])
(re-frame/dispatch-sync [::update-product-stock :eggs 2]) (re-frame/dispatch-sync [::update-product-stock :eggs 2])
) )
;;;;;;;; Backend mocks
(re-frame/reg-event-fx
::clear-database
(fn [_ _]
(mocks/purge-items)
(.reload js/location)))
(re-frame/reg-event-fx
::generate-database
(fn [_ _]
(mocks/generate-items)
{:fx [[:dispatch [::start-loading]]
[:dispatch [::fetch-stock]]
[:dispatch [::fetch-orders]]]}))