refactored code.

This commit is contained in:
didier amyot committed 2020-09-10 14:39:26 -04:00
1 parent ed7f4ef668
commit cfb63cb473
3 files changed
+37 -231

No files matched your search

@@ -1,4 +1,4 @@
(ns skirmish.ledger.routes
(ns skirmish.ledger.controllers
(:require [skirmish.rest.util :refer [build-response get-body cors-middleware]]
[skirmish.ledger.data-layer :as data-layer]
[skirmish.routes.core :as routes]
@@ -178,6 +178,6 @@
(routes/GET :ledger/securities [] (get-available-securities conn))
(routes/POST :ledger/securities [] (create-security conn))
(routes/GET :ledger/security [] (get-security conn))
(routes/PUT :ledger/security [] (update-security conn))) ;; not done
;(routes/POST :ledger/security [] (emit-securities conn))) ;; done
(routes/PUT :ledger/security [] (update-security conn)) ;; TODO
(routes/POST :ledger/security [] (emit-securities conn)))
(not-found "not found")))
+4 -199
View File
@@ -1,42 +1,11 @@
(ns skirmish.ledger.core
(:require [org.httpkit.server :refer [run-server]]
[skirmish.routes.core :as routes]
[skirmish.ledger.controllers :as controllers]
[compojure.handler :refer [api]]
[skirmish.util :refer [block-thread]]
[slingshot.slingshot :refer [try+ throw+]]
[compojure.route :refer [not-found]]
[skirmish.exchange-gateway.exchange-gateway :refer [create-routes-for-exchange]]
[skirmish.rest.util :refer [build-response get-body cors-middleware]]
[skirmish.ledger.data-layer :as data-layer]
[skirmish.rest.util :refer [cors-middleware]]
[skirmish.ledger.accounts]
[datomic.api :as d]
[taoensso.timbre :as timbre]
[compojure.core :as compojure]
[clojure.spec.alpha :as s]
[infix.macros :refer [infix]]
[ring.middleware.cors :refer [wrap-cors]]))
;; the ledger is the central
;; an :account-id is the public key of a
;; an :account-secret is an :account-id private key
(defn get-account-body [request {sec-id :sec-id amount :amount account-public-key :account-public-key}]
(if (nil? sec-id)
(build-response request {:status 404})
(let [sec-url (str "/api/1/ledger/securities/" (name sec-id))
beautify-account { :amount amount
:sec-id sec-id
:account-public-key account-public-key}
ret {:account beautify-account :sec-url sec-url}]
(build-response request ret))))
(defn get-account [conn]
"Returns the value of an account."
(fn [{{account-id :account-id} :route-params :as request}]
(let [db (d/db conn)
account-info (data-layer/get-amount-in-account db account-id)]
(get-account-body request account-info))))
[clojure.spec.alpha :as s]))
(s/def ::transaction (s/cat :type #{:transfer-immediately}
:from-account-id string?
@@ -45,12 +14,6 @@
(s/def ::transactions (s/coll-of ::transaction))
(defn extract-transactions [raw-transactions]
(if (s/valid? ::transactions raw-transactions)
(s/conform ::transactions raw-transactions)
(throw+ {:error-type :4XX :status 400 :body {:error "Invalid transaction format."}})))
(s/def ::security (s/cat :sec-id keyword?
:interest-rate nat-int?
:type #{:equity :currency}
@@ -58,172 +21,14 @@
: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 prettify-security [e db]
(let [{interest-rate :skirmish.security/interest-rate
description :skirmish.security/description
id :db/id
sec-id :skirmish.security/sec-id
symbol :skirmish.security/symbol
name :skirmish.security/name
number-of-decimal :skirmish.security/number-of-decimal
type_ :skirmish.security/type} e
interest-rate-history (data-layer/get-interest-rate-history db id)
volumes (d/q '[:find ?amount ?date
:in $ ?id
:where [?e :skirmish.ledger/sec ?id]
[?e :skirmish.ledger/amount ?amount ?tx]
[?tx :db/txInstant ?date]]
db id)
now (new java.util.Date)
volume (reduce + 0 (map #(data-layer/compute-face-value (first %) interest-rate-history (second %) now) volumes))]
{:sec-id sec-id
:interest-rate interest-rate
:volume (or volume 0)
:name name
:interest-rate-history interest-rate-history
:symbol symbol
:number-of-decimal number-of-decimal
: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])]
(if (-> e :db/id nil?)
(throw+ {:error-type :4XX :status 404 :body {:error "Security not found."}})
(prettify-security e db))))
(defn create-security [conn]
"Create a fresh new security."
(fn [{body :body :as request}]
(try+
(let [[sec-id interest-rate] (-> body get-body get-new-security)
new-sec @(data-layer/create-security conn 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}]
(try+
(let [sec-id (keyword sec-id-str)]
(build-response request (get-security-from-db conn sec-id)))
(catch [:error-type :4XX] e (build-response request e))
(catch [:error-type :5XX] e (build-response request e)))))
(defn update-security [conn]
"Update a security with new features."
(fn [request]
(try+
(catch [:error-type :4XX] e (build-response request e))
(catch [:error-type :5XX] e (build-response request e)))))
(defn get-available-securities-body [securities]
(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)];;
{:securities securities-info}))
(defn get-available-securities [conn]
(fn [request]
(let [db (d/db conn)
securities (data-layer/get-all-securities-id db)]
(build-response request (get-available-securities-body securities)))))
(s/def ::emit-securities (s/cat :sec-id keyword?
:amount nat-int?
:account string?))
(defn get-emit-securities-request [[amount account] sec-id]
(if (s/valid? ::emit-securities [sec-id amount account])
[:skirmish.ledger/emit-securities sec-id amount account]
(throw+ {:error-type :4XX :status 400 :body {:error "Invalid emit request."}})))
(defn emit-securities [conn]
"Emit securities in the specified wallet. This is the only way to
create securities.
payload
[:amount :account]"
(fn [{{sec-id-str :sec-id} :route-params body :body :as request}]
(try+
(let [emit-request (-> body get-body (get-emit-securities-request (keyword sec-id-str)))]
@(d/transact conn [emit-request])
(build-response request {}))
(catch [:error-type :4XX] e (build-response request e))
(catch [:error-type :5XX] e (build-response request e)))))
(defn get-available-accounts-body [accounts]
(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)]
{:accounts securities-info}))
(defn get-available-accounts [conn]
(fn [request]
(let [db (d/db conn)]
(build-response request (get-available-accounts-body (data-layer/get-all-account-public-key db))))))
(defn get-api-description [request]
(let [body [{:url "ledger/accounts" :name "accounts"}
{:url "ledger/securities" :name "securities"}]]
(build-response request body)))
(defn get-transactions [conn]
(fn [request]
(let [all-pending-transactions []]
(build-response request all-pending-transactions))))
(defn create-transactions [conn]
(fn [{body :body :as request}]
(try+
(let [transactions (-> body get-body extract-transactions)
_ @(d/transact conn [[:skirmish.ledger/create-transactions transactions]])])
(catch [:error-type :4XX] e (build-response request e))
(catch [:error-type :5XX] e (build-response request e)))))
(defn create-routes-for-ledger [conn]
(compojure/routes
(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/transactions [] (get-transactions conn))
(routes/POST :ledger/transactions [] (create-transactions 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 conn)]
api-routes (controllers/create-routes-for-ledger conn)]
(timbre/info "Running skirmish.ledger.core server with config: " ledger-config)
(run-server
(-> api-routes api cors-middleware)
+30 -29
View File
@@ -1,6 +1,7 @@
(ns skirmish.ledger.test-core
(:require [midje.sweet :refer :all]
[skirmish.ledger.data-layer :as data-layer]
[skirmish.ledger.controllers :as controllers]
[skirmish.ledger.accounts :refer [create-connection schema]]
[skirmish.rest.util :refer [build-response]]
[skirmish.ledger.core :refer :all]
@@ -45,7 +46,7 @@
;;
(defn create-some-securities [conn]
(let [cs (create-security conn)
(let [cs (controllers/create-security conn)
create-security-1 {:body (edn->stream (get mock-securities 0)) :headers {"accept" "application/edn"}}
create-security-2 {:body (edn->stream (get mock-securities 1)) :headers {"accept" "application/edn"}}
create-security-3 {:body (edn->stream (get mock-securities 2)) :headers {"accept" "application/edn"}}
@@ -58,7 +59,7 @@
(defn create-some-accounts [conn]
(let [es (emit-securities conn)
(let [es (controllers/emit-securities conn)
emit-sec-req-1 {:body (edn->stream [100 "1-sec-1"]) :route-params {:sec-id "sec-1"}}
emit-sec-req-2 {:body (edn->stream [200 "2-sec-1"]) :route-params {:sec-id "sec-1"}}
emit-sec-req-3 {:body (edn->stream [1000 "1-sec-2"]) :route-params {:sec-id "sec-2"}}
@@ -78,7 +79,7 @@
(let [conn (create-conn)
security [:new-currency 100 :currency "currency for test" "rot13" "NC"]
request {:body (edn->stream security) :headers {"accept" "application/edn"}}
resp ((create-security conn) request)]
resp ((controllers/create-security conn) request)]
resp => (spec-check ::ok)
resp => (location? "/api/1/ledger/securities/new-currency")))
@@ -86,8 +87,8 @@
(let [conn (create-conn)
security [:new-currency 100 :currency "currency for test" "rot13" "NC"]
request {:body (edn->stream security) :headers {"accept" "application/edn"}}
resp-1 ((create-security conn) request)
resp-2 ((create-security conn) request)]
resp-1 ((controllers/create-security conn) request)
resp-2 ((controllers/create-security conn) request)]
resp-1 => (spec-check ::ok)
resp-2 => (spec-check ::status-400)))
@@ -98,9 +99,9 @@
(let [conn (create-conn)
security [:new-currency 100 :currency "currency for test" "rot13" "NC"]
create-request {:body (edn->stream security) :headers {"accept" "application/edn"}}
_ ((create-security conn) create-request)
_ ((controllers/create-security conn) create-request)
request {:route-params {:sec-id :new-currency} :headers {"accept" "application/edn"}}
resp ((get-security conn) request)
resp ((controllers/get-security conn) request)
expt {:sec-id :new-currency
:interest-rate 100
:name nil
@@ -115,18 +116,18 @@
(fact "we should not be able to get-security if it does not exists"
(let [conn (create-conn)
request {:route-params {:sec-id :new-currency} :headers {"accept" "application/edn"}}
resp ((get-security conn) request)]
resp ((controllers/get-security conn) request)]
resp => (spec-check ::status-404)
resp => #(-> % :sec-id nil?)))
(fact "we should be able to get a list of all securities"
(let [conn (create-conn)
request {:route-params {} :headers {"accept" "application/edn"}}
empty-res ((get-available-securities conn) request)
empty-res ((controllers/get-available-securities conn) request)
new-currency [:new-currency 100 :currency "currency for test" "rot13" "NC"]
create-security-1 {:body (edn->stream new-currency) :headers {"accept" "application/edn"}}
_ ((create-security conn) create-security-1)
one-res ((get-available-securities conn) request)]
_ ((controllers/create-security conn) create-security-1)
one-res ((controllers/get-available-securities conn) request)]
empty-res => (spec-check ::ok)
one-res => (spec-check ::ok)
(body empty-res) => {:securities []}
@@ -137,7 +138,7 @@
_ (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 ((controllers/get-available-securities conn) request)]
(->> resp body :securities (map :sec-id) sort) => [:apac :apax :apbm :apdr :apmh :dapm])))
@@ -147,27 +148,27 @@
req-1 {:body (edn->stream [10 :not-a-string]) :route-params {:sec-id "sec-1"} :headers {"accept" "application/edn"}}
req-2 {:body (edn->stream [-10 "foo"]) :route-params {:sec-id "sec-1"} :headers {"accept" "application/edn"}}
req-3 {:body (edn->stream [90 "account-id"]) :route-params {:sec-id "invalid-sec-id"} :headers {"accept" "application/edn"}}]
((emit-securities conn) req-1) => (spec-check ::status-400)
((emit-securities conn) req-2) => (spec-check ::status-400)
((emit-securities conn) req-3) => (spec-check ::status-404)))
((controllers/emit-securities conn) req-1) => (spec-check ::status-400)
((controllers/emit-securities conn) req-2) => (spec-check ::status-400)
((controllers/emit-securities conn) req-3) => (spec-check ::status-404)))
(fact "should create new account if targeted account does not exists"
(let [conn (create-some-securities (create-conn))
get-account-req {:route-params {:account-id "new-account-id"} :headers {"accept" "application/edn"}}
emit-sec-req {:body (edn->stream [90 "new-account-id"]) :route-params {:sec-id "sec-1"} :headers {"accept" "application/edn"}}]
((get-account conn) get-account-req) => (spec-check ::status-404)
((emit-securities conn) emit-sec-req) => (spec-check ::ok)
(body ((get-account conn) get-account-req)) => #(-> % :account :amount (= 90))))
((controllers/get-account conn) get-account-req) => (spec-check ::status-404)
((controllers/emit-securities conn) emit-sec-req) => (spec-check ::ok)
(body ((controllers/get-account conn) get-account-req)) => #(-> % :account :amount (= 90))))
(fact "should add to account if targeted account exists"
(let [conn (create-some-securities (create-conn))
get-account-req {:route-params {:account-id "new-account-id"} :headers {"accept" "application/edn"}}
emit-sec-req-1 {:body (edn->stream [90 "new-account-id"]) :route-params {:sec-id "sec-1"} :headers {"accept" "application/edn"}}
emit-sec-req-2 {:body (edn->stream [100 "new-account-id"]) :route-params {:sec-id "sec-1"} :headers {"accept" "application/edn"}}]
((get-account conn) get-account-req) => (spec-check ::status-404)
((emit-securities conn) emit-sec-req-1) => (spec-check ::ok)
((emit-securities conn) emit-sec-req-2) => (spec-check ::ok)
(body ((get-account conn) get-account-req)) => #(-> % :account :amount (= 190)))))
((controllers/get-account conn) get-account-req) => (spec-check ::status-404)
((controllers/emit-securities conn) emit-sec-req-1) => (spec-check ::ok)
((controllers/emit-securities conn) emit-sec-req-2) => (spec-check ::ok)
(body ((controllers/get-account conn) get-account-req)) => #(-> % :account :amount (= 190)))))
(facts "about accounts"
@@ -178,28 +179,28 @@
{: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)))
((controllers/get-available-accounts conn) req) => (spec-check ::ok)
(-> ((controllers/get-available-accounts conn) req) body :accounts sec->hash-set) => (sec->hash-set expt)))
(fact "we should be able to get account"
(let [conn (create-some-securities-and-accounts (create-conn))
req {:route-params {:account-id "2-sec-1"} :headers {"accept" "application/edn"}}]
((get-account conn) req) => (spec-check ::ok)
(body ((get-account conn) req)) => {:account {:account-public-key "2-sec-1" :amount 200 :sec-id :sec-1}
((controllers/get-account conn) req) => (spec-check ::ok)
(body ((controllers/get-account conn) req)) => {:account {:account-public-key "2-sec-1" :amount 200 :sec-id :sec-1}
:sec-url "/api/1/ledger/securities/sec-1"}))
(fact "the interest should be payed when getting the account"
(let [conn (create-some-securities-and-accounts (create-conn))
req {:route-params {:account-id "2-sec-1"} :headers {"accept" "application/edn"}}]
((get-account conn) req) => (spec-check ::ok)
(body ((get-account conn) req)) => {:account {:account-public-key "2-sec-1" :amount 200 :sec-id :sec-1}
((controllers/get-account conn) req) => (spec-check ::ok)
(body ((controllers/get-account conn) req)) => {:account {:account-public-key "2-sec-1" :amount 200 :sec-id :sec-1}
:sec-url "/api/1/ledger/securities/sec-1"}))
(fact "we should not get an account if it does not exists"
(let [conn (create-some-securities-and-accounts (create-conn))
req {:route-params {:account-id "2-sec-10"} :headers {"accept" "application/edn"}}]
((get-account conn) req) => (spec-check ::status-404))))
((controllers/get-account conn) req) => (spec-check ::status-404))))
(facts "about interest computation"