something

This commit is contained in:
didier amyot committed 2018-11-01 19:40:17 -04:00
1 parent 440206e924
commit b972e6e69f
15 files changed
+230 -141

No files matched your search

+2 -1
View File
@@ -13,6 +13,7 @@
[org.clojure/math.combinatorics "0.1.4"]
[com.datomic/datomic-free "0.9.5697"]
[com.google.guava/guava "22.0"]
[javax.xml.bind/jaxb-api "2.2.11"]
;; server
[aero "1.1.3"]
@@ -64,7 +65,6 @@
:min-lein-version "2.5.0"
:uberjar-name "skirmish.jar"
:main skirmish.core
:jvm-opts ["--add-modules" "java.xml.bind"]
:clean-targets ^{:protect false}
[:target-path
[:cljsbuild :builds :app :compiler :output-dir]
@@ -128,6 +128,7 @@
:profiles {:dev {:repl-options {:init-ns skirmish.wui.repl
:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}
:java-source-paths ["src/java"]
:dependencies [[midje "1.9.1"]
[stylefruits/gniazdo "1.0.1"] ;; only used in benchmark
+1 -32
View File
@@ -6,7 +6,7 @@
:instrument-spec true
:datomic
{:transactor-url "datomic:free://localhost:4334/skirmish"}
{:transactor-url "datomic:free://localhost:4334/skirmish.ledger"}
;; all the kafka configuration goes bellow.
:kafka
@@ -40,40 +40,9 @@
:max-ws 524288
:datomic {:transactor-url "datomic:free://localhost:4334/skirmish.broker"}}
:wui
{:port #long #or [#env WUI_PORT 3000]}
;; list of tradable securities
:securities
[{:sec-id :dapm
:currency :dapm
:symbol "ლ"
:name "didier amyot paper money"
:interest-rate 1.1 ;; 10% per day
:emitter "emitter publick key"
:description "The default currency."}
{:sec-id :foo
:currency :dapm
:name "the foo company"
:description "Sell names to developer."}
{:sec-id :bar
:currency :dapm
:name "bar international"
:description "Steal company."}
{:sec-id :toto
:currency :dapm
:name "La compagnie des toto"
:description "Serait trop long pour ecrire ici."}
{:sec-id :tata
:currency :dapm
:name "Unicorn startup"
:description "ambition: conquerir le monde!"}]
;; stupid parameters about benchmarking tools
:benchmark {
:number-of-requests-per-securities 30}}
+1 -1
View File
@@ -50,7 +50,7 @@
:where [_ :skirmish.broker.user/email ?email]] db)
xf (comp cat (map #(hash-map :email % :url (str "/api/1/users/" %))))]
(build-response request
(doall ((sequence xf emails)))))
{:users (doall (sequence xf emails))}))
(catch [:error-type :4XX] e (build-response request e))
(catch [:error-type :5XX] e (build-response request e)))))
+4 -5
View File
@@ -9,8 +9,8 @@
: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
:skirmish.broker.user/accounts []])]
: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}])})}])
@@ -27,7 +27,7 @@
: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/doc "The security id of the account."}
{
:db/ident :skirmish.broker.account/private-key
:db/valueType :db.type/string
@@ -44,10 +44,9 @@
: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."}])
:db/doc "The list of the accounts own by the user."}])
(def schema (-> []
+52 -6
View File
@@ -111,10 +111,56 @@
(concat create-securities)
(concat print-securities)))
(defn create-connection [uri]
(let [new-db? (d/create-database uri)
conn (d/connect uri)]
;(when new-db?
@(d/transact conn schema)
conn))
(defn add-default-currencies [conn]
(let [dapm #:skirmish.security{:sec-id :dapm
:name "didier amyot paper money"
:type :currency
:description "The official currency of the 'Skirmish Shitty Exchange'."
:emitter "dapm private key"
:number-of-decimal 2
:symbol "რ"
:interest-rate 10}
apbm #:skirmish.security{:sec-id :apbm
:name "apache beam"
:type :equity
:description "Unified programming model to define and execute data processing pipelines, including ETL, batch and stream (continuous) processing."
:emitter "apbm private key"
:number-of-decimal 0}
apac #:skirmish.security{:sec-id :apac
:name "apache accumulo"
:type :equity
:description "A sorted, distributed key/value store that provides robust, scalable data storage and retrieval."
:emitter "apac private key"
:number-of-decimal 0}
apax #:skirmish.security{:sec-id :apax
:name "apache apex"
:type :equity
:description "Enterprise-grade unified stream and batch processing engine."
:emitter ":apax private key"
:number-of-decimal 0}
apdr #:skirmish.security{:sec-id :apdr
:name "apache drill"
:type :equity
:description "Schema-free SQL Query Engine for Hadoop, NoSQL and Cloud Storage."
:emitter ":apdr private key"
:number-of-decimal 0}
apmh #:skirmish.security{:sec-id :apmh
:name "apache mahout"
:type :equity
:description "For Creating Scalable Performant Machine Learning Applications."
:emitter ":apmh private key"
:number-of-decimal 0}]
@(d/transact conn [dapm apbm apac apax apdr apmh])))
(defn create-connection
([uri init-when-new?]
(let [new-db? (d/create-database uri)
conn (d/connect uri)]
@(d/transact conn schema)
(if (and init-when-new? new-db?)
(add-default-currencies conn))
conn))
([uri]
(create-connection uri true)))
+21 -19
View File
@@ -1,5 +1,6 @@
(ns skirmish.ledger.core
(:require [org.httpkit.server :refer [run-server]]
[skirmish.routes.core :as routes]
[compojure.handler :refer [api]]
[skirmish.util :refer [block-thread]]
[slingshot.slingshot :refer [try+ throw+]]
@@ -9,7 +10,7 @@
[skirmish.ledger.accounts]
[datomic.api :as d]
[taoensso.timbre :as timbre]
[compojure.core :refer :all]
[compojure.core :as compojure]
[clojure.spec.alpha :as s]
[ring.middleware.cors :refer [wrap-cors]]))
@@ -88,10 +89,10 @@
:type type_
:description description}))
(defn get-security-from-db [conn sec-id]
(let [db (d/db conn)
e (d/pull db '[*] [:skirmish.security/sec-id sec-id])]
(println (str e))
(if (-> e :db/id nil?)
(throw+ {:error-type :4XX :status 404 :body {:error "Security not found."}})
(prettify-security e db))))
@@ -100,13 +101,14 @@
"Create a fresh new security."
(fn [{body :body :as request}]
(try+
(let [[sec-id interest-rate] (-> body get-body get-new-security)]
@(d/transact conn [[:skirmish.ledger/create-security sec-id interest-rate]])
(let [[sec-id interest-rate] (-> body get-body get-new-security)
new-sec @(d/transact conn [[:skirmish.ledger/create-security sec-id interest-rate]])]
(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)))))
(defn get-security [conn]
"Returns the security."
(fn [{{sec-id-str :sec-id} :route-params :as request}]
@@ -126,7 +128,7 @@
(defn get-available-securities-body [securities]
(let [sec-id->url #(str "/api/1/ledger/securities/" (name %))
(let [sec-id->url #(routes/get-path-for :ledger/security {:sec-id (name %)})
to-security-description #(hash-map :sec-id % :url (sec-id->url %))
xf (comp cat (map to-security-description))
securities-info (sequence xf securities)];;
@@ -161,7 +163,7 @@
(defn get-available-accounts-body [accounts]
(let [sec-id->url #(str "/api/1/ledger/accounts/" (name %))
(let [sec-id->url #(routes/get-path-for :ledger/account {:account-id (name %)})
to-security-description #(hash-map :account-public-key % :url (sec-id->url %))
xf (comp cat (map to-security-description))
securities-info (sequence xf accounts)];;
@@ -180,25 +182,25 @@
(build-response request body)))
(defn create-routes-for-ledger [securities conn]
(routes
(context "/api/1/ledger" []
(GET "/" get-api-description)
(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
(defn create-routes-for-ledger [conn]
(compojure/routes
;; (routes/get-context :ledger)
(routes/context :ledger []
(routes/GET :ledger/home [] get-api-description)
(routes/GET :ledger/accounts [] (get-available-accounts conn))
(routes/GET :ledger/account [] (get-account conn))
(routes/GET :ledger/securities [] (get-available-securities conn)) ;; done
(routes/POST :ledger/securities [] (create-security conn)) ;; done
(routes/GET :ledger/security [] (get-security conn)) ;; done
(routes/PUT :ledger/security [] (update-security conn)) ;; not done
(routes/POST :ledger/security [] (emit-securities conn))) ;; done
(not-found "not found")))
(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)]
api-routes (create-routes-for-ledger conn)]
(timbre/info "Running skirmish.ledger.core server with config: " ledger-config)
(run-server
(-> api-routes api cors-middleware)
@@ -1,4 +1,4 @@
(ns skirmish.routes)
(ns skirmish.routes.api)
(defn get-app-for [all-routes k]
"An app consists on a name, a prefix and some routes."
@@ -16,16 +16,15 @@
"Returns what compojure expects to have."
(->> k (get-route-for all-routes) :path))
(defn replace-part [part params]
(defn- replace-part [part params]
(if (clojure.string/starts-with? part ":")
(-> part (subs 1) keyword params)
part))
(defn path-for [p params]
(defn- path-for [p params]
(let [parts (clojure.string/split p #"/")]
(clojure.string/join "/" (map #(replace-part % params) parts))))
(defn get-context [all-routes c]
(let [get-app #(get-app-for all-routes %)]
(-> c
@@ -34,7 +33,6 @@
get-app
:path)))
(defn get-path-for
"Returns a link to a location by name."
([all-routes k]
@@ -42,6 +40,7 @@
([all-routes k params]
(let [app (get-app-for all-routes k)
route (get-route-for all-routes k)
p (str (:path app) (:path route))]
(path-for p params))))
p (str (:path app) (:path route))
domain (get app :domain "")]
(str domain (path-for p params)))))
+44
View File
@@ -0,0 +1,44 @@
(ns skirmish.routes.core
(:require [skirmish.routes.api :as routes-api]
[compojure.core :as compojure]))
(def ledger-routes {
:name :ledger
:domain "http://localhost:1604" ;; should somehow be in some config
:path "/api/1/ledger"
:routes [{:name :home :path "/"}
{:name :accounts :path "/accounts"}
{:name :account :path "/accounts/:account-id"}
{:name :securities :path "/securities"}
{:name :security :path "/securities/:sec-id"}]})
(def all-routes [ledger-routes])
(def get-context (partial routes-api/get-context all-routes))
(def get-compojure-path (partial routes-api/get-compojure-path all-routes))
(def get-path-for (partial routes-api/get-path-for all-routes))
(defmacro GET [path args & body]
(compojure/compile-route :get
(get-compojure-path path)
args
body))
(defmacro POST [path args & body]
(compojure/compile-route :post
(get-compojure-path path)
args
body))
(defmacro PUT [path args & body]
(compojure/compile-route :put
(get-compojure-path path)
args
body))
(defmacro context [path args & routes]
(let [resolved-path (get-context path)]
(concat `(compojure/context ~resolved-path ~args) routes)))
-16
View File
@@ -11,19 +11,3 @@
(defn block-thread []
"Block the current thread until chuck norris gets weaker."
(async/<!! (async/chan)))
(defn- get-overlapping-for-specific-event [event sorted-events]
(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 []]
(if (seq events)
(let [first-event (first events)
rest-events (rest events)
overlapping-events (get-overlapping-for-specific-event first-event rest-events)
overlapping-pairs (map vector (repeat first-event) overlapping-events)
new-ret (if (seq overlapping-pairs) (into ret overlapping-pairs) ret)]
(recur rest-events new-ret))
ret))))
@@ -1,6 +1,6 @@
(ns skirmish.wui.components.securities-page
(:require [cljs-http.client :as http]
[cljs.core.async :refer [<!]]
[cljs.core.async :refer [<!] :as async]
[reagent.core :refer [atom]])
(:require-macros [cljs.core.async.macros :refer [go]]))
@@ -18,21 +18,62 @@
[:td (:name sec)]
[:td (:description sec)]]))
(defn get-securities [securities]
(go (let [url "http://localhost:1603/api/1/securities"
response (<! (http/get url {:with-credentials? false}))]
(reset! securities (get-in response [:body :securities])))))
(defn get-all-securities-urls []
(let [ret (async/chan)]
(go (let [url "http://localhost:1604/api/1/ledger/securities"
response (<! (http/get url {:with-credentials? false}))
urls (map :url (get-in response [:body :securities]))]
(async/onto-chan ret urls)))
ret))
(defn create-security-table [securities]
[:table.table.is-hoverable.is-fullwidth
table-head
[:tbody
(for [sec securities] (create-row sec))]])
(defn get-all-securities []
(let [ret (async/chan)
urls (get-all-securities-urls)]
(async/go-loop [url (async/<! urls)]
(when-let [url (async/<! urls)]
(async/go
(let [sec (:body (<! (http/get url {:with-credentials? false})))]
(println sec)
(async/>! ret sec)))
(recur url)))
ret))
(defn get-securities [a-securities]
(let [securities (get-all-securities)]
(async/go-loop [security (async/<! securities)]
(when (some? security)
(swap! a-securities #(conj % security))
(recur (async/<! securities))))))
(defn create-currencies-table [securities]
[:div
[:div.content
[:h3 "Currencies"]]
[:table.table.is-hoverable.is-fullwidth
table-head
[:tbody
(for [sec securities :when (= (:type sec) "currency")] (create-row sec))]]])
(defn create-equities-table [securities]
[:div
[:div.content
[:h3 "Equities"]]
[:table.table.is-hoverable.is-fullwidth
table-head
[:tbody
(for [sec securities :when (= (:type sec) "equity")] (create-row sec))]]])
(defn create-securities-table [securities]
[:div
[create-currencies-table securities]
[:br]
[create-equities-table securities]])
(defn security-table []
(let [securities (atom [])]
(get-securities securities)
#(create-security-table @securities)))
#(create-securities-table @securities)))
(defn page [state]
(security-table))
@@ -30,17 +30,8 @@
[:li "Make sure the ledger can have accounts"]
[:li "when a new user signs in, he should receive some sort of money"]
[:li "\u22EE"]
[:li "implement buy/sell securities."]]]
[:li "Cleaning up..."
[:ul
[:li "All the urls on the client should be at the same place."]
[:li "We should have a nice way to handle routes on the server."]]]
[:li "Implement Ledger"
[:ol
[:li "implement buy/sell securities."]
[:li "Use ledger as source of through in each sub server."]
[:li "Update web ui for integrating securities with ledger."]
[:li "Ledger should compute interest rate."]
[:li "implement transactions in ledger."]
[:li "implement escrow."]]]]])
+21
View File
@@ -0,0 +1,21 @@
(ns skirmish.broker.db-test
(:require [midje.sweet :refer :all]
[orchestra.spec.test :as st]
[datomic.api :as d]
[skirmish.broker.db :refer [create-connection]]))
(st/instrument)
(defn create-conn []
(let [mem-url "datomic:mem://skirmish"]
(d/delete-database mem-url)
(create-connection mem-url)))
(facts "about the db functions."
(fact "should be able to create new users"
(let [conn (create-conn)
email "foo@gmail.com"
_ @(d/transact conn [[:skirmish.broker.user/create-user email]])
db (d/db conn)
user (d/pull db '[*] [:skirmish.broker.user/email email])]
(:skirmish.broker.user/email user) => email)));
+15 -7
View File
@@ -24,7 +24,7 @@
(defn create-conn []
(let [mem-url "datomic:mem://skirmish"]
(d/delete-database mem-url)
(create-connection mem-url)))
(create-connection mem-url false)))
@@ -40,7 +40,7 @@
(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"}}
@@ -126,7 +126,15 @@
empty-res => (spec-check ::ok)
one-res => (spec-check ::ok)
(body empty-res) => {:securities []}
(body one-res) => {:securities [{:sec-id :new-currency :url "/api/1/ledger/securities/new-currency"}]})))
(body one-res) => {:securities [{:sec-id :new-currency :url "http://localhost:1604/api/1/ledger/securities/new-currency"}]}))
(fact "it should add default currencies when creating new db"
(let [mem-url "datomic:mem://skirmish-in-test"
_ (d/delete-database mem-url)
conn (create-connection mem-url true)
request {:route-params {} :headers {"accept" "application/edn"}}
resp ((get-available-securities conn) request)]
(->> resp body :securities (map :sec-id) sort) => [:apac :apax :apbm :apdr :apmh :dapm])))
(facts "about the emitting securities"
@@ -162,10 +170,10 @@
(fact "we should be able to get list of all accounts"
(let [conn (create-some-securities-and-accounts (create-conn))
req {:route-params {} :headers {"accept" "application/edn"}}
expt [{:account-public-key "1-sec-1" :url "/api/1/ledger/accounts/1-sec-1"}
{:account-public-key "1-sec-2" :url "/api/1/ledger/accounts/1-sec-2"}
{:account-public-key "2-sec-1" :url "/api/1/ledger/accounts/2-sec-1"}
{:account-public-key "2-sec-2" :url "/api/1/ledger/accounts/2-sec-2"}]]
expt [{:account-public-key "1-sec-1" :url "http://localhost:1604/api/1/ledger/accounts/1-sec-1"}
{:account-public-key "1-sec-2" :url "http://localhost:1604/api/1/ledger/accounts/1-sec-2"}
{:account-public-key "2-sec-1" :url "http://localhost:1604/api/1/ledger/accounts/2-sec-1"}
{:account-public-key "2-sec-2" :url "http://localhost:1604/api/1/ledger/accounts/2-sec-2"}]]
((get-available-accounts conn) req) => (spec-check ::ok)
(-> ((get-available-accounts conn) req) body :accounts sec->hash-set) => (sec->hash-set expt)))
@@ -1,19 +1,21 @@
(ns skirmish.test-routes
(ns skirmish.routes.test-api
(:require [midje.sweet :refer :all]
[skirmish.routes :refer :all]))
[skirmish.routes.api :refer :all]))
(def my-ledger-routes {
:name :ledger
:domain "http://localhost:1604" ;; should somehow be in some config
:path "/api/1/ledger"
:routes [{:name :home :path "/"}
{:name :accounts :path "/accounts"}
{:name :account :path "/accounts/:account-id"}]})
(def my-all-routes
[my-ledger-routes])
(def wui-routes {
:name :wui
:domain "http://localhost:1534"
:path "/api/1/wui"
:routes [{:name :home :path "/home"}]})
(def my-all-routes [my-ledger-routes wui-routes])
(facts "About routes"
(fact "it should be easy to get routes"
@@ -21,8 +23,8 @@
(fact "it should be able to retrieve routes"
(get-path-for my-all-routes :ledger/accounts) => "/api/1/ledger/accounts"
(get-path-for my-all-routes :ledger/account {:account-id "one-two"}) => "/api/1/ledger/accounts/one-two")
(get-path-for my-all-routes :ledger/account {:account-id "one-two"}) => "/api/1/ledger/accounts/one-two"
(get-path-for my-all-routes :wui/home) => "http://localhost:1534/api/1/wui/home")
(fact "it should be able to retrieve the contect"
(get-context my-all-routes :ledger) => "/api/1/ledger"))
;;
-18
View File
@@ -4,21 +4,3 @@
;; midje testing
(facts "About events overlapping"
(let [evt-1 #:calendar.event{:start 10 :end 15}
evt-2 #:calendar.event{:start 12 :end 17}
evt-3 #:calendar.event{:start 15 :end 25}
evt-4 #:calendar.event{:start 30 :end 40}
evt-5 #:calendar.event{:start 33 :end 34}]
(fact "about edge cases"
(get-overlapping-events nil) => empty?
(get-overlapping-events []) => empty?)
(fact "about regular situations"
(get-overlapping-events [evt-1 evt-2]) => [[evt-1 evt-2]]
(get-overlapping-events [evt-1 evt-2 evt-3]) => [[evt-1 evt-2] [evt-2 evt-3]]
(get-overlapping-events [evt-4 evt-5]) => [[evt-4 evt-5]])))
;;