implement basic login in the wui
This commit is contained in:
1 parent
d4bc2490ff
commit
addff66470
20 files changed
+217
-83
No files matched your search
@@ -3,7 +3,7 @@
|
||||

|
||||
|
||||
A stock exchange simulator. The scope of this project is not yet defined but it should
|
||||
allow end user(s?) to exchange securities via the common exchange rules.
|
||||
allow end user(s?) to exchange securities via a kind of stock exchange
|
||||
|
||||
|
||||
## Getting Started
|
||||
@@ -26,8 +26,15 @@ All configuration are set in the `resources/config.edn` file.
|
||||
Configuration uses [aero](https://github.com/juxt/aero). The configuration
|
||||
file should be self documented.
|
||||
|
||||
|
||||
### Technologies
|
||||
|
||||
#### Big architecture
|
||||
|
||||
* [docker](https://www.docker.com/) because it is trendy.
|
||||
* [docker-compose](https://docs.docker.com/compose/) to manage system dependencies.
|
||||
* [jvm](https://en.wikipedia.org/wiki/Java_virtual_machine) because nothing beats it.
|
||||
|
||||
#### Server
|
||||
* [clojure](https://clojure.org/) for programming language
|
||||
* [kafka](http://kafka.apache.org/) for message broker/database
|
||||
@@ -35,9 +42,30 @@ file should be self documented.
|
||||
|
||||
#### In the browser
|
||||
* [clojure script](https://clojurescript.org/) for programming language
|
||||
* [sparks](https://aftertheflood.com/projects/sparks/) for trivial charts
|
||||
* [reagent](https://reagent-project.github.io/) as frontend framework. Simple and powerful.
|
||||
* [metricsgraphicsjs](http://metricsgraphicsjs.org/) for complex charts
|
||||
* [dc-js](http://dc-js.github.io/dc.js/) for some charting.
|
||||
* [dc-js](http://dc-js.github.io/dc.js/) for some charting. Looks awesome!.
|
||||
|
||||
|
||||
### Architecture shenanigans
|
||||
|
||||
As any personal project, over engineering it the only solution. Moreover the
|
||||
scope of the project is unknown. Consequently, there is a plethora of
|
||||
micro-servicesh.
|
||||
|
||||
|
||||
#### Micro services
|
||||
|
||||
A micro service does one thing, and does it well. Micro services communicate to the world
|
||||
via REST api and collaborate together via a global state kept in kafka.
|
||||
|
||||
| name | description | REST |
|
||||
|:----------------------------|:--------------------------------------------------------------------------|:-----:|
|
||||
| `skirmish.exchange` | Exchange engine. Knows nothing about what it trades. | `yes` |
|
||||
| `skirmish.exchange-gateway` | Public api to `skirmish.exchange`. Dispatch an order to the proper place | `no` |
|
||||
| `skirmish.ledger` | Source of truth for who owns what. | `yes` |
|
||||
| `skirmish.bookkeeper` | Source of truth for the marker data. Publishes market data to the world. | `yes` |
|
||||
|
||||
|
||||
|
||||
## License
|
||||
|
||||
@@ -33,6 +33,13 @@
|
||||
:max-body 524288 ;; 512kb, small but enough
|
||||
:max-ws 524288}
|
||||
|
||||
:api-broker
|
||||
{:port #long #or [#env BROKER_SERVER_PORT 1605]
|
||||
:ip "0.0.0.0"
|
||||
:max-body 524288 ;; 512kb, small but enough
|
||||
:max-ws 524288}
|
||||
|
||||
|
||||
:wui
|
||||
{:port #long #or [#env WUI_PORT 3000]}
|
||||
|
||||
|
||||
@@ -4564,8 +4564,8 @@ a.navbar-item,
|
||||
.navbar.is-transparent .navbar-item.has-dropdown.is-active .navbar-link, .navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:hover .navbar-link {
|
||||
background-color: transparent !important; }
|
||||
.navbar.is-transparent .navbar-dropdown a.navbar-item:hover {
|
||||
background-color: #0a0a0a;
|
||||
color: #0a0a0a; }
|
||||
background-color: #363636;
|
||||
color: none; }
|
||||
.navbar.is-transparent .navbar-dropdown a.navbar-item.is-active {
|
||||
background-color: #0a0a0a;
|
||||
color: #00d1b2; }
|
||||
@@ -4582,7 +4582,7 @@ a.navbar-item,
|
||||
.navbar-item.has-dropdown-up .navbar-link::after {
|
||||
transform: rotate(135deg) translate(0.25em, -0.25em); }
|
||||
.navbar-item.has-dropdown-up .navbar-dropdown {
|
||||
border-bottom: 2px solid #dbdbdb;
|
||||
border-bottom: 1px solid #00d1b2;
|
||||
border-radius: 6px 6px 0 0;
|
||||
border-top: none;
|
||||
bottom: 100%;
|
||||
@@ -4604,10 +4604,10 @@ a.navbar-item,
|
||||
justify-content: flex-end;
|
||||
margin-left: auto; }
|
||||
.navbar-dropdown {
|
||||
background-color: white;
|
||||
background-color: #363636;
|
||||
border-bottom-left-radius: 6px;
|
||||
border-bottom-right-radius: 6px;
|
||||
border-top: 2px solid #dbdbdb;
|
||||
border-top: 1px solid #00d1b2;
|
||||
box-shadow: 0 8px 8px rgba(10, 10, 10, 0.1);
|
||||
display: none;
|
||||
font-size: 0.875rem;
|
||||
@@ -4622,8 +4622,8 @@ a.navbar-item,
|
||||
.navbar-dropdown a.navbar-item {
|
||||
padding-right: 3rem; }
|
||||
.navbar-dropdown a.navbar-item:hover {
|
||||
background-color: #0a0a0a;
|
||||
color: #0a0a0a; }
|
||||
background-color: #363636;
|
||||
color: none; }
|
||||
.navbar-dropdown a.navbar-item.is-active {
|
||||
background-color: #0a0a0a;
|
||||
color: #00d1b2; }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
(ns skirmish.bookkeeper.bookkeeper
|
||||
(:require [skirmish.exchange.exchange :refer [empty-book create-exchange-for-security]]
|
||||
(:require [skirmish.exchange.exchange :refer [empty-book]]
|
||||
[org.httpkit.server :refer [run-server with-channel send! close on-close]]
|
||||
[compojure.route :refer [not-found]]
|
||||
[compojure.handler :refer [api]]
|
||||
@@ -73,14 +73,14 @@
|
||||
(fn [{{sec-id-str :sec-id} :route-params :as request}]
|
||||
(with-channel request ws-chan
|
||||
(let [status-to-listen-on (get-status-to-listen-on request)
|
||||
order-channel (get-new-order-channel exchange-apis (keyword sec-id-str) status-to-listen-on)]
|
||||
order-channel (get-new-order-channel @exchange-apis (keyword sec-id-str) status-to-listen-on)]
|
||||
(chan-to-websocket request order-channel ws-chan)))))
|
||||
|
||||
|
||||
(defn listen-to-trades [exchange-apis]
|
||||
(fn [{{sec-id-str :sec-id} :route-params :as request}]
|
||||
(with-channel request ws-chan
|
||||
(let [order-channel (get-new-trades-channel exchange-apis (keyword sec-id-str))]
|
||||
(let [order-channel (get-new-trades-channel @exchange-apis (keyword sec-id-str))]
|
||||
(chan-to-websocket request order-channel ws-chan)))))
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
|
||||
|
||||
(defn create-routes-for-bookkeeper [exchange-apis config]
|
||||
(let [books (map-values #(create-book %) exchange-apis)]
|
||||
(let [books (map-values #(create-book %) @exchange-apis)]
|
||||
(routes
|
||||
(context "/api/1" []
|
||||
(GET "/" [] api-description)
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
(:require [org.httpkit.server :refer [run-server]]
|
||||
[compojure.handler :refer [api]]
|
||||
[skirmish.util :refer [block-thread]]
|
||||
[skirmish.middleware.core :refer [create-map-exchange-apis]]
|
||||
[skirmish.bookkeeper.bookkeeper :refer [create-routes-for-bookkeeper]]
|
||||
[ring.middleware.cors :refer [wrap-cors]]))
|
||||
|
||||
(defn -main [config exchange-apis]
|
||||
(defn -main [config securities-api exchange-apis-mult-chan]
|
||||
(let [bookkeeper-config (:api-bookkeeper config)
|
||||
exchange-apis (create-map-exchange-apis exchange-apis-mult-chan)
|
||||
api-routes (create-routes-for-bookkeeper exchange-apis config)]
|
||||
(println "Running skirmish.bookkeeper.core server with bellow config\n" bookkeeper-config)
|
||||
(println bookkeeper-config)
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
(ns skirmish.broker.core)
|
||||
(ns skirmish.broker.core
|
||||
(:require [org.httpkit.server :refer [run-server]]
|
||||
[compojure.handler :refer [api]]
|
||||
[skirmish.util :refer [block-thread]]
|
||||
[skirmish.exchange-gateway.exchange-gateway :refer [create-routes-for-exchange]]
|
||||
[skirmish.middleware.core :refer [create-map-exchange-apis]]
|
||||
[ring.middleware.cors :refer [wrap-cors]]))
|
||||
|
||||
|
||||
(def portfolio {:dapm {:amount 10}
|
||||
:foo {:amount 5 :long-exposure 10 :short-exposure 7}
|
||||
:bar {:amount 100 :long-exposure 10 :short-exposure 7}})
|
||||
|
||||
|
||||
(defn -main [config exchange-apis])
|
||||
(defn -main [config securities-api exchange-apis]
|
||||
(let [webserver-config (:api-broker config)
|
||||
api-routes (-> exchange-apis create-map-exchange-apis create-routes-for-exchange)]
|
||||
(println "Running skirmish.exchange-gateway.core server with bellow config\n" webserver-config)
|
||||
(run-server (api api-routes) webserver-config)))
|
||||
@@ -6,7 +6,8 @@
|
||||
[skirmish.bookkeeper.core]
|
||||
[skirmish.exchange-gateway.core]
|
||||
[skirmish.ledger.core]
|
||||
[skirmish.middleware.core :refer [create-exchange-apis-for]]
|
||||
[skirmish.middleware.core :refer [create-exchange-apis-for
|
||||
create-securities-api-for]]
|
||||
[aero.core :refer (read-config)]
|
||||
[orchestra.spec.test :as st]
|
||||
[clojure.core.async :as async])
|
||||
@@ -20,21 +21,21 @@
|
||||
:ledger skirmish.ledger.core/-main
|
||||
:wui skirmish.wui.server/-main})
|
||||
|
||||
(defn run-all-targets-in-separate-threads [target-fns config exchange-apis]
|
||||
(defn run-all-targets-in-separate-threads [target-fns config securities-api exchange-apis]
|
||||
(println "running multiple targets. This is okay in dev but horrible in production")
|
||||
(println (clojure.string/join "\n" target-fns))
|
||||
(let [threads (map #(async/thread (% config exchange-apis)) target-fns)
|
||||
(let [threads (map #(async/thread (% config securities-api exchange-apis)) target-fns)
|
||||
finished-threads (async/merge threads)]
|
||||
(loop [finished-thread (async/<!! finished-threads)]
|
||||
(when (some? finished-thread)
|
||||
(recur (async/<!! finished-threads))))))
|
||||
|
||||
|
||||
(defn run-all-targets [target-fns config]
|
||||
(let [exchange-apis (create-exchange-apis-for config)]
|
||||
(let [securities-api (create-securities-api-for config)
|
||||
exchange-apis (create-exchange-apis-for config securities-api)]
|
||||
(if (= (count target-fns) 1)
|
||||
((first target-fns) config exchange-apis)
|
||||
(run-all-targets-in-separate-threads target-fns config exchange-apis))))
|
||||
((first target-fns) config securities-api exchange-apis)
|
||||
(run-all-targets-in-separate-threads target-fns config securities-api exchange-apis))))
|
||||
|
||||
(defn -main [& argv]
|
||||
(let [config (-> "config.edn" clojure.java.io/resource read-config)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
(ns skirmish.exchange.core
|
||||
(:require [skirmish.util :refer [block-thread]]
|
||||
[clojure.core.async :as async]
|
||||
[skirmish.middleware.core :refer [create-exchange-apis-for]]
|
||||
[skirmish.exchange.exchange :refer [create-exchange-for-security empty-book]]))
|
||||
|
||||
@@ -15,7 +16,9 @@
|
||||
trades-submitter
|
||||
orders-submitter)))
|
||||
|
||||
(defn -main [config exchange-apis]
|
||||
(doseq [{sec-id :sec-id} (:securities config)]
|
||||
(create-exchange-for (get exchange-apis sec-id)))
|
||||
(defn -main [config securities-api exchange-apis-mult-chan]
|
||||
(let [ch (async/chan)]
|
||||
(async/tap exchange-apis-mult-chan ch)
|
||||
(async/go-loop [exchange-api (async/<! ch)]
|
||||
(create-exchange-for exchange-api)))
|
||||
(block-thread))
|
||||
@@ -3,12 +3,13 @@
|
||||
[compojure.handler :refer [api]]
|
||||
[skirmish.util :refer [block-thread]]
|
||||
[skirmish.exchange-gateway.exchange-gateway :refer [create-routes-for-exchange]]
|
||||
[skirmish.middleware.core :refer [create-map-exchange-apis]]
|
||||
[ring.middleware.cors :refer [wrap-cors]]))
|
||||
|
||||
|
||||
(defn -main [config exchange-apis]
|
||||
(defn -main [config securities-api exchange-apis]
|
||||
(let [webserver-config (:api-server config)
|
||||
api-routes (create-routes-for-exchange exchange-apis)]
|
||||
api-routes (-> exchange-apis create-map-exchange-apis create-routes-for-exchange)]
|
||||
(println "Running skirmish.exchange-gateway.core server with bellow config\n" webserver-config)
|
||||
(run-server (api api-routes) webserver-config)))
|
||||
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
(ns skirmish.exchange-gateway.exchange-gateway
|
||||
(:require [skirmish.exchange.exchange :refer [empty-book create-exchange-for-security]]
|
||||
[org.httpkit.server :refer [run-server with-channel send! close on-close]]
|
||||
[compojure.route :refer [not-found]]
|
||||
[compojure.handler :refer [api]]
|
||||
(:require [compojure.route :refer [not-found]]
|
||||
[compojure.core :refer :all]
|
||||
[skirmish.order] ;; for spec side effect
|
||||
[skirmish.rest.util :refer [build-response build-response-body get-body]]
|
||||
[skirmish.rest.util :refer [build-response get-body]]
|
||||
[clojure.spec.alpha :as s]
|
||||
[kinsky.client]
|
||||
[slingshot.slingshot :refer [try+ throw+]]
|
||||
[clojure.core.async :refer [chan go go-loop close! <! >!! sub]]))
|
||||
[clojure.core.async :refer [>!!]]))
|
||||
|
||||
|
||||
(def order-id (atom -1))
|
||||
@@ -37,7 +34,7 @@
|
||||
(fn [{{sec-id-str :sec-id} :route-params body :body :as request}]
|
||||
(try+
|
||||
(let [asked-order (get-body body)
|
||||
exchange-api (get-exchange-api (keyword sec-id-str) exchange-apis)
|
||||
exchange-api (get-exchange-api (keyword sec-id-str) @exchange-apis)
|
||||
incoming-order-channel (:new-order-submitter exchange-api)
|
||||
order (validate-order asked-order (:sec-id exchange-api))]
|
||||
(>!! incoming-order-channel order)
|
||||
|
||||
@@ -80,9 +80,7 @@
|
||||
(build-response request {:body {:success true}
|
||||
:headers {"location" (str "/api/1" "/ledger/securities/" (name sec-id))}}))
|
||||
(catch [:error-type :4XX] e (build-response request e))
|
||||
(catch [:error-type :5XX] e (build-response request e))
|
||||
(catch Object _
|
||||
(println (:throwable &throw-context) "unexpected error")))))
|
||||
(catch [:error-type :5XX] e (build-response request e)))))
|
||||
|
||||
(defn get-security [conn]
|
||||
"Returns the security."
|
||||
@@ -169,7 +167,7 @@
|
||||
(not-found "not found")))
|
||||
|
||||
|
||||
(defn -main [config exchange-apis]
|
||||
(defn -main [config securities-api exchange-apis]
|
||||
(let [ledger-config (:ledger-server config)
|
||||
conn (skirmish.ledger.accounts/create-connection (-> config :datomic :transactor-url))
|
||||
api-routes (create-routes-for-ledger (:securities config) conn)]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
(ns skirmish.middleware.core
|
||||
(:require [clojure.core.async :as async]
|
||||
[kinsky.async]
|
||||
[clojure.core.async :refer [go-loop <! sub chan]]
|
||||
[kinsky.client]))
|
||||
|
||||
(defn create-chan-based-exchange-api-for [sec-id]
|
||||
@@ -8,6 +9,7 @@
|
||||
trades (async/chan)
|
||||
orders (async/chan)]
|
||||
{:sec-id sec-id
|
||||
:disabled false
|
||||
:new-order-publisher new-order ;; anything published here will be processed as
|
||||
:new-order-submitter new-order
|
||||
:trades-publisher (async/pub trades :book.order/type)
|
||||
@@ -15,12 +17,44 @@
|
||||
:orders-publisher (async/pub orders :book.order/status)
|
||||
:orders-submitter orders}))
|
||||
|
||||
(def api-creators {})
|
||||
|
||||
(defn create-exchange-apis-for [config]
|
||||
;; todo: spec this function
|
||||
(let [security-definitions (map :sec-id (:securities config))
|
||||
create-exchange-api-for (get api-creators (:middleware config) create-chan-based-exchange-api-for)
|
||||
f (fn [sec] [sec (create-exchange-api-for sec)])
|
||||
ret (into {} (map f security-definitions))]
|
||||
ret))
|
||||
(defn create-chan-based-security-api [config]
|
||||
(let [securities (async/chan)]
|
||||
{:securities-publisher (async/mult securities)
|
||||
:securities-submitter securities}))
|
||||
|
||||
(def security-api-creators {})
|
||||
|
||||
(defn create-securities-api-for [config]
|
||||
(let [creator (get security-api-creators (:middleware config) create-chan-based-security-api)]
|
||||
(creator config)))
|
||||
|
||||
(defn update-exchange-apis [create-exchange-api-for sec]
|
||||
(let [sec-id (:sec-id sec)
|
||||
disabled? (get sec :disabled false)]
|
||||
(if disabled?
|
||||
{:sec-id sec-id :disabled true}
|
||||
(create-exchange-api-for sec-id))))
|
||||
|
||||
(def exchange-api-creators {})
|
||||
|
||||
(defn create-exchange-apis-for [config securities-api]
|
||||
(let [ret (async/chan)
|
||||
securities-ch (async/chan)
|
||||
securities-publisher (:securities-publisher securities-api)
|
||||
create-exchange-api-for (get exchange-api-creators (:middleware config) create-chan-based-exchange-api-for)]
|
||||
(async/tap securities-publisher securities-ch)
|
||||
(go-loop [sec (<! securities-ch)]
|
||||
(async/>! ret (update-exchange-apis create-exchange-api-for sec))
|
||||
(recur (<! securities-ch)))
|
||||
(async/mult ret)))
|
||||
|
||||
(defn create-map-exchange-apis [exchange-apis-mult-chan]
|
||||
(let [c (async/chan)
|
||||
ret (atom {})]
|
||||
(async/tap exchange-apis-mult-chan chan)
|
||||
(async/go-loop [exchange-api (async/<! c)]
|
||||
(if (:disabled? exchange-api)
|
||||
(swap! ret #(dissoc % (:sec-id exchange-api)))
|
||||
(swap! ret #(assoc % (:sec-id exchange-api) exchange-api))))
|
||||
ret))
|
||||
@@ -16,7 +16,6 @@
|
||||
(let [event-end (:calendar.event/end event)]
|
||||
(take-while #(< (:calendar.event/start %) event-end) sorted-events)))
|
||||
|
||||
|
||||
(defn get-overlapping-events [events]
|
||||
(let [sorted-events (sort-by :calendar.event/start events)]
|
||||
(loop [events sorted-events ret []]
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
[ring.adapter.jetty :refer [run-jetty]])
|
||||
(:gen-class))
|
||||
|
||||
(defn -main [config exchange-apis]
|
||||
(defn -main [config securities-api exchange-apis]
|
||||
(let [port 3000]
|
||||
(run-jetty app {:port port :join? false})))
|
||||
@@ -7,10 +7,24 @@
|
||||
"Gentrify palo santo enamel pin, tumeric jianbing thundercats shaman green juice chambray yr. Kale chips synth meditation chambray thundercats helvetica, cornhole whatever kickstarter ennui pop-up lo-fi godard. Austin unicorn brunch woke hammock. Coloring book kombucha occupy tofu wayfarers."])
|
||||
|
||||
|
||||
(defn login [state]
|
||||
(let [login-field (atom (get state :user ""))
|
||||
on-change #(reset! login-field (-> % .-target .-value))]
|
||||
(fn []
|
||||
[:div.field
|
||||
[:div.control
|
||||
[:label.label "Username"]
|
||||
[:div.field.is-grouped
|
||||
[:input.input.is-success {:type "text" :value @login-field :on-change on-change :style {:margin-right "1em"}}]
|
||||
[:div.control
|
||||
[:a.button.is-link.has-background-primary {:href (str "/home/" @login-field)} "login"]]]]])))
|
||||
|
||||
|
||||
(defn page [state]
|
||||
[:div
|
||||
[:section.section
|
||||
[:div.container
|
||||
[:h1.title "lorem ipsum"]
|
||||
[login state]
|
||||
(for [sentence lorem-ipsum]
|
||||
[:p sentence])]]])
|
||||
@@ -8,26 +8,21 @@
|
||||
|
||||
(defn page []
|
||||
[:div.content
|
||||
[:ul
|
||||
[:li "General"
|
||||
[:ul
|
||||
[:li "write trivial mock strategy framework."]
|
||||
[:li "write mock strategies."]
|
||||
[:li "think of a solution where to store data."]]]
|
||||
[:li "Web user interface"
|
||||
[:ul
|
||||
[:li done "Add simple pages for every section"]
|
||||
[:li "Securities"
|
||||
[:ul
|
||||
[:li "display all the available securities"]
|
||||
[:li "display the book for selected securities"]]]]]
|
||||
[:li "Exchange"
|
||||
[:ul
|
||||
[:li done "make an exchange that kind of work"]
|
||||
[:li "spec the exchange"]]]
|
||||
[:li "Book keeper"
|
||||
[:ul
|
||||
[:li "Find a place to store information about securities"]]]
|
||||
[:li "Brooker"
|
||||
[:ul
|
||||
[:li "Write a small broker"]]]]])
|
||||
[:ol
|
||||
[:li "Broker"
|
||||
[:p "the brooker should use the exchange-gateway and ledge as apis. Nothing else."]
|
||||
[:ol
|
||||
[:li done "implement basic login in the wui"]
|
||||
[:li on-it "implement basic web server"]
|
||||
[:li "implement stupid login."]
|
||||
[:li "implement account notion. Use ledger for that."]
|
||||
[:li "implement buy/sell securities."]]]
|
||||
|
||||
[:li "Implement Ledger"
|
||||
[:ol
|
||||
[:li "Use ledger as source of through in each sub server."]
|
||||
[:li "Update web ui for integrating securities with ledger."]
|
||||
[:li "Create a basic broker that supports..."]
|
||||
[:li "Ledger should compute interest rate."]
|
||||
[:li "implement transactions in ledger."]
|
||||
[:li "implement escrow."]]]]])
|
||||
@@ -10,6 +10,26 @@
|
||||
"is-active"))
|
||||
|
||||
|
||||
(defn- user-dropdown [state]
|
||||
(when (:user @state)
|
||||
(let [icon [:i.fa.fa-sign-out {:aria-hidden true}]]
|
||||
[:div.navbar-item.has-dropdown.is-hoverable
|
||||
[:a.navbar-link {} (str "[ " (:user @state) " ]")]
|
||||
[:div.navbar-dropdown
|
||||
[:a.navbar-item {:href "/home/"} icon " logout"]]])))
|
||||
|
||||
|
||||
(defn- login [state]
|
||||
(let [icon [:i.fa.fa-sign-out {:aria-hidden true}]]
|
||||
[:a.navbar-item.is-tab {:href "/"} icon " login"]))
|
||||
|
||||
|
||||
(defn user-placeholder [state]
|
||||
(if (:user @state)
|
||||
[user-dropdown state]
|
||||
[login state]))
|
||||
|
||||
|
||||
(defn top-bar [state]
|
||||
[:nav.navbar.is-dark.is-boxed {:style {:margin-bottom "1em"}}
|
||||
[:div.container
|
||||
@@ -20,4 +40,5 @@
|
||||
[:a.navbar-item.is-tab {:href "/portfolio" :class (top-bar-class state :view-portfolio)} "View Portfolio"]
|
||||
[:a.navbar-item.is-tab {:href "/todo" :class (top-bar-class state :todo)} "Todo"]]
|
||||
[:div.navbar-end
|
||||
[:a.navbar-item.is-tab {} "Skirmish Shitty Exchange"]]]])
|
||||
[:a.navbar-item.is-tab {} "Skirmish Shitty Exchange"]
|
||||
[user-placeholder state]]]])
|
||||
@@ -26,6 +26,15 @@
|
||||
(session/put! :current-page #'pages/home-page)
|
||||
(swap! state #(into % {:page :home})))
|
||||
|
||||
(secretary/defroute "/home/:username" [username]
|
||||
(session/put! :current-page #'pages/home-page)
|
||||
(swap! state #(into % {:page :home :user username})))
|
||||
|
||||
(secretary/defroute "/home/" []
|
||||
(session/put! :current-page #'pages/home-page)
|
||||
(swap! state #(into % {:page :home :user nil})))
|
||||
|
||||
|
||||
(secretary/defroute "/securities" []
|
||||
(session/put! :current-page #'pages/securities-page)
|
||||
(swap! state #(into % {:page :securities})))
|
||||
|
||||
@@ -8,11 +8,19 @@
|
||||
|
||||
@import "../../node_modules/bulma/sass/utilities/_all";
|
||||
|
||||
// main color changes
|
||||
$background: $black;
|
||||
$body-background-color: $black;
|
||||
$text: $white-ter;
|
||||
$link: $turquoise;
|
||||
|
||||
// navbar color change
|
||||
$navbar-dropdown-background-color: $grey-darker;
|
||||
$navbar-dropdown-border-top: 1px solid $turquoise !default;
|
||||
$navbar-dropdown-item-hover-color: none;
|
||||
$navbar-dropdown-item-hover-background-color: $grey-darker;
|
||||
|
||||
// table color changes
|
||||
$table-background-color: $grey-dark;
|
||||
$table-cell-heading-color: $grey-darker;
|
||||
$table-row-hover-background-color: $black-ter;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
(ns skirmish.middleware.test-core
|
||||
(:require [midje.sweet :refer :all]
|
||||
[clojure.core.async :refer [>!! <!!] :as async]
|
||||
[skirmish.middleware.core :refer :all]))
|
||||
|
||||
(defn all? [xf]
|
||||
@@ -7,7 +8,18 @@
|
||||
|
||||
(facts "we should have a async/chan base middleware for developing."
|
||||
(fact "It should exchanges for each security in the config"
|
||||
(let [config {:securities [{:sec-id :molly} {:sec-id :pepper}]}
|
||||
exchanges (create-exchange-apis-for config)]
|
||||
(keys exchanges) => [:molly :pepper]
|
||||
(vals exchanges) => #(every? map? %))))
|
||||
(let [config {}
|
||||
securities [{:sec-id :molly :type :equity} {:sec-id :pepper :type :currency} {:sec-id :molly :type :equity :disabled true}]
|
||||
securities-exchange (create-securities-api-for config)
|
||||
securities-submitter (:securities-submitter securities-exchange)
|
||||
exchanges-mult (create-exchange-apis-for config securities-exchange)
|
||||
exchanges (async/chan)
|
||||
sec-id-and-disabled (juxt :sec-id :disabled)]
|
||||
(async/tap exchanges-mult exchanges)
|
||||
(>!! securities-submitter (first securities))
|
||||
|
||||
(-> exchanges <!! sec-id-and-disabled) => [:molly false]
|
||||
(>!! securities-submitter (second securities))
|
||||
(-> exchanges <!! sec-id-and-disabled) => [:pepper false]
|
||||
(>!! securities-submitter (last securities))
|
||||
(-> exchanges <!! sec-id-and-disabled) => [:molly true])))
|
||||
Reference in new issue
Block a user