diff --git a/README.md b/README.md index d195359..d2cf33f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Skrimish -![logo](doc/logo.svg "Skrimish") +![logo](https://erpscan.com/wp-content/themes/erpscancom/img/hq__amsterdam.svg "Skrimish") A stock exchange simulator. The scope of this project is not yet defined but it should allow end user(s?) to exchange securities via a kind of stock exchange diff --git a/resources/public/images/amsterdam.svg b/resources/public/images/amsterdam.svg new file mode 100644 index 0000000..a025826 --- /dev/null +++ b/resources/public/images/amsterdam.svg @@ -0,0 +1,55 @@ + + + + + + hq__amsterdam + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/clj/skirmish/broker/broker.clj b/src/clj/skirmish/broker/broker.clj index 92c1ec5..a5b2f87 100644 --- a/src/clj/skirmish/broker/broker.clj +++ b/src/clj/skirmish/broker/broker.clj @@ -18,17 +18,25 @@ (catch [:error-type :5XX] e (build-response request e))))) +(defn add-account-urls [user] + (let [accounts (:skirmish.broker.user/accounts user) + url #(->> % :skirmish.broker.account/id (str "http://localhost:1604/api/1/ledger/accounts/")) + ret (map #(assoc % :skirmish.broker.account/url (url %)) accounts)] + (assoc user :skirmish.broker.user/accounts ret))) + (defn get-user [conn] (fn [{{email :email} :route-params :as request}] (try+ (timbre/info "getting user with email" email) (let [db (d/db conn) - users (d/q '[:find [(pull ?e [*])] + users (d/q '[:find (pull ?e [:skirmish.broker.user/email + {:skirmish.broker.user/accounts [:skirmish.broker.account/id + :skirmish.broker.account/sec-id]}]) . :in $ ?email :where [?e :skirmish.broker.user/email ?email]] db email)] (if (nil? users) (build-response request {:status 404 :body {:error (str "cannot find user with email " email)}}) - (build-response request (first users)))) + (build-response request (add-account-urls users)))) (catch [:error-type :4XX] e (build-response request e)) (catch [:error-type :5XX] e (build-response request e))))) diff --git a/src/clj/skirmish/broker/db.clj b/src/clj/skirmish/broker/db.clj index 47a260e..fe85b0f 100644 --- a/src/clj/skirmish/broker/db.clj +++ b/src/clj/skirmish/broker/db.clj @@ -9,29 +9,55 @@ :db/fn (d/function {:lang "clojure" :requires ['[slingshot.slingshot]] :params '[db email] - :code '(let [existing-user (d/pull db '[:db/id] [:skirmish.broker.user/email email])] + :code '(let [existing-user (d/pull db '[:db/id] [:skirmish.broker.user/email email + :skirmish.broker.user/accounts []])] (when (-> existing-user :db/id some?) (slingshot.slingshot/throw+ {:error-type :4XX :status 400 :body {:error "A user with this email already exists."}})) [#:skirmish.broker.user{:email email}])})}]) +(def accounts-schema [ + { + :db/ident :skirmish.broker.account/id + :db/unique :db.unique/identity + :db/valueType :db.type/uuid + :db/cardinality :db.cardinality/one + :db/doc "The id of an account."} + { + :db/ident :skirmish.broker.account/sec-id + :db/valueType :db.type/keyword + :db/cardinality :db.cardinality/one + :db/doc "The security if of the account."} + { + :db/ident :skirmish.broker.account/private-key + :db/valueType :db.type/string + :db/cardinality :db.cardinality/one + :db/doc "The private key of the account."}]) + + (def user-schema [ { :db/ident :skirmish.broker.user/email :db/unique :db.unique/identity :db/valueType :db.type/string :db/cardinality :db.cardinality/one + :db/doc "The email of the user; used as id."} + { + :db/ident :skirmish.broker.user/accounts + :db/unique :db.unique/identity + :db/valueType :db.type/ref + :db/cardinality :db.cardinality/many :db/doc "The email of the user; used as id."}]) (def schema (-> [] (concat user-schema) - (concat create-user))) + (concat create-user) + (concat accounts-schema))) (defn create-connection [uri] (let [new-db? (d/create-database uri) conn (d/connect uri)] - (when new-db? - @(d/transact conn schema)) + @(d/transact conn schema) conn)) diff --git a/src/clj/skirmish/exchange/core.clj b/src/clj/skirmish/exchange/core.clj index 8972d25..1cf2e28 100644 --- a/src/clj/skirmish/exchange/core.clj +++ b/src/clj/skirmish/exchange/core.clj @@ -16,6 +16,7 @@ trades-submitter orders-submitter))) + (defn -main [config securities-api exchange-apis-mult-chan] (let [ch (async/chan)] (async/tap exchange-apis-mult-chan ch) diff --git a/src/clj/skirmish/ledger/accounts.clj b/src/clj/skirmish/ledger/accounts.clj index a629b27..7ea7fe8 100644 --- a/src/clj/skirmish/ledger/accounts.clj +++ b/src/clj/skirmish/ledger/accounts.clj @@ -54,7 +54,6 @@ :db/cardinality :db.cardinality/one :db/unique :db.unique/identity :db/doc "The type of security. For instance :equity or :currency."} - { :db/ident :skirmish.security/description :db/valueType :db.type/string @@ -81,7 +80,6 @@ (def ledger-schema [ - { :db/ident :skirmish.ledger/owner-public-key :db/unique :db.unique/identity @@ -101,14 +99,12 @@ :db/cardinality :db.cardinality/one :db/doc "The sec kept in this account."}]) - (def schema (-> [] (concat schema-securities) (concat ledger-schema) (concat create-securities) (concat print-securities))) - (defn create-connection [uri] (let [new-db? (d/create-database uri) conn (d/connect uri)] diff --git a/src/clj/skirmish/ledger/core.clj b/src/clj/skirmish/ledger/core.clj index 03a161e..87dea0a 100644 --- a/src/clj/skirmish/ledger/core.clj +++ b/src/clj/skirmish/ledger/core.clj @@ -58,16 +58,23 @@ :emitter string? :symbol (s/? string?))) - (defn get-new-security [raw-securities] (if (s/valid? ::security raw-securities) raw-securities (throw+ {:error-type :4XX :status 400 :body {:error "Invalid security format."}}))) (defn get-security-from-db [conn sec-id] - (let [e (d/pull (d/db conn) '[*] [:skirmish.security/sec-id sec-id]) - {interest-rate :skirmish.security/interest-rate id :db/id} e - ret {:sec-id sec-id :interest-rate interest-rate}] + (let [db (d/db conn) + e (d/pull db '[*] [:skirmish.security/sec-id sec-id]) + {interest-rate :skirmish.security/interest-rate + description :skirmish.security/description + id :db/id + type_ :skirmish.security/type} e + volume (d/q '[:find (sum ?a) . + :where [?e :skirmish.ledger/amount ?a] + [?e :skirmish.ledger/owner-public-key ?pk]] + db) + ret {:sec-id sec-id :interest-rate interest-rate :volume volume :type type_ :description description}] (if (nil? id) (throw+ {:error-type :4XX :status 404 :body {:error "Security not found."}}) ret))) @@ -156,15 +163,15 @@ (defn create-routes-for-ledger [securities conn] (routes - (context "/api/1" [] + (context "/api/1/ledger" [] (GET "/" get-api-description) - (GET "/ledger/accounts" [] (get-available-accounts conn)) - (GET "/ledger/accounts/:account-id" [] (get-account conn)) - (GET "/ledger/securities" [] (get-available-securities conn)) ;; done - (POST "/ledger/securities" [] (create-security conn)) ;; done - (GET "/ledger/securities/:sec-id" [] (get-security conn)) ;; done - (PUT "/ledger/securities/:sec-id" [] (update-security conn)) ;; not done - (POST "/ledger/securities/:sec-id" [] (emit-securities conn))) ;; done + (GET "/accounts" [] (get-available-accounts conn)) + (GET "/accounts/:account-id" [] (get-account conn)) + (GET "/securities" [] (get-available-securities conn)) ;; done + (POST "/securities" [] (create-security conn)) ;; done + (GET "/securities/:sec-id" [] (get-security conn)) ;; done + (PUT "/securities/:sec-id" [] (update-security conn)) ;; not done + (POST "/securities/:sec-id" [] (emit-securities conn))) ;; done (not-found "not found"))) diff --git a/src/cljs/skirmish/wui/components/home_page.cljs b/src/cljs/skirmish/wui/components/home_page.cljs index e3d2756..2aff50d 100644 --- a/src/cljs/skirmish/wui/components/home_page.cljs +++ b/src/cljs/skirmish/wui/components/home_page.cljs @@ -4,12 +4,6 @@ [cljs.core.async :refer [> s str (re-matches email-pattern) some?))) @@ -49,7 +43,5 @@ [:div [:section.section [:div.container - [:h1.title "lorem ipsum"] - [login state] - (for [[i sentence] (map vector (range) lorem-ipsum)] - [:p {:key i} sentence])]]]) \ No newline at end of file + [:img {:src "/images/amsterdam.svg"}] + [login state]]]]) \ No newline at end of file diff --git a/src/cljs/skirmish/wui/components/portfolio_page.cljs b/src/cljs/skirmish/wui/components/portfolio_page.cljs index d99a2e7..7186c6c 100644 --- a/src/cljs/skirmish/wui/components/portfolio_page.cljs +++ b/src/cljs/skirmish/wui/components/portfolio_page.cljs @@ -2,6 +2,7 @@ (:require [reagent.core :refer [atom]] [skirmish.wui.components.secunit :refer [secunit]])) + (defn make-portfolio-summary-table [portfolio] [:table [:thead @@ -13,9 +14,11 @@ [:td [secunit (:sec-id sec-summary) (:amount sec-summary)]] [:td [secunit :dapm (:face-value sec-summary)]]])]]) -(defn page [] + +(defn page [{{accounts :skirmish.broker.user/accounts} :user}] (let [porfolio [{:id 12 :sec-id :foo :amount 100 :face-value 1265} {:id 54 :sec-id :toto :amount 100 :face-value 1265} {:id 66 :sec-id :foo :amount 100 :face-value 1265}]] [:div.content + [:pre (str accounts)] (make-portfolio-summary-table porfolio)])) diff --git a/src/cljs/skirmish/wui/components/todo_page.cljs b/src/cljs/skirmish/wui/components/todo_page.cljs index 5878115..8fad8d7 100644 --- a/src/cljs/skirmish/wui/components/todo_page.cljs +++ b/src/cljs/skirmish/wui/components/todo_page.cljs @@ -15,12 +15,14 @@ [:li done "implement basic login in the wui"] [:li done "implement basic web server"] [:li done "implement stupid login on server side."] - [:li on-it "link the logic of the client-server login logic"] - [:li "add the notion of 'accounts' in the server. An - user should have many accounts; one for each security - he owns"] - [:li "make the " [:a {:href "/portfolio"} "portfolio"] " page representing the use accounts"] + [:li done "link the logic of the client-server login logic"] + [:li done "add the notion of 'accounts' in the server. An + user should have many accounts; one for each security + he owns"] + [:li on-it "make the " [:a {:href "/portfolio"} "portfolio"] " page representing the use accounts"] + [:li "Make sure the ledger can have accounts"] [:li "when a new user logs in, he should receive some sort of money"] + [:li "\u22EE"] [:li "implement buy/sell securities."]]] [:li "Cleaning up..." diff --git a/src/cljs/skirmish/wui/core.cljs b/src/cljs/skirmish/wui/core.cljs index 904851a..16b79ee 100644 --- a/src/cljs/skirmish/wui/core.cljs +++ b/src/cljs/skirmish/wui/core.cljs @@ -4,7 +4,6 @@ [skirmish.wui.components.topbar :refer [top-bar]] [reagent.session :as session] [cljs-http.client :as http] - ;[taoensso.timbre :as timbre :refer-macros [info warn]] [cljs.core.async :refer [ ret))) diff --git a/test/clj/skirmish/exchange/exchange_test.clj b/test/clj/skirmish/exchange/exchange_test.clj index 79c0622..01d9104 100644 --- a/test/clj/skirmish/exchange/exchange_test.clj +++ b/test/clj/skirmish/exchange/exchange_test.clj @@ -1,5 +1,5 @@ (ns skirmish.exchange.exchange-test - (:import [skirmish.PersistentSortedList]) + (:import (skirmish PersistentSortedList)) (:require [midje.sweet :refer :all] [orchestra.spec.test :as st] [skirmish.exchange.exchange :refer :all] @@ -9,7 +9,7 @@ (st/instrument) -(def PersistentSortedList? (comp #(= % skirmish.PersistentSortedList) type)) +(def PersistentSortedList? (comp #(= % PersistentSortedList) type)) (facts "the exchange as an whole" (fact "process-sell-order should work when book empty" diff --git a/test/clj/skirmish/ledger/test_core.clj b/test/clj/skirmish/ledger/test_core.clj index 64564fa..c104d59 100644 --- a/test/clj/skirmish/ledger/test_core.clj +++ b/test/clj/skirmish/ledger/test_core.clj @@ -41,8 +41,6 @@ (def mock-currencies [[:zimbabwean-dollar 1000000 :currency "zimbabwean dollar" "rot13" "zd"]]) - - (defn create-some-securities [conn] (let [cs (create-security conn) create-security-1 {:body (edn->stream (get mock-securities 0)) :headers {"accept" "application/edn"}} @@ -101,7 +99,7 @@ request {:route-params {:sec-id :new-currency} :headers {"accept" "application/edn"}} resp ((get-security conn) request)] resp => (spec-check ::ok) - (body resp) => {:sec-id :new-currency :interest-rate 100})) + (body resp) => {:sec-id :new-currency :interest-rate 100 :description nil :type nil :volume nil})) (fact "we should not be able to get-security if it does not exists" (let [conn (create-conn)