Skip to content

Commit

Permalink
Use malli for validation & coercion
Browse files Browse the repository at this point in the history
Fixes #25 & closes #12
  • Loading branch information
cap10morgan committed Mar 6, 2023
1 parent 46d515a commit c30889b
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 84 deletions.
4 changes: 2 additions & 2 deletions deps.edn
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
org.slf4j/slf4j-api {:mvn/version "2.0.5"}
metosin/reitit {:mvn/version "0.5.18"}
metosin/muuntaja {:mvn/version "0.6.8"}
metosin/spec-tools {:mvn/version "0.10.5"}
metosin/malli {:mvn/version "0.10.1"}
ring-cors/ring-cors {:mvn/version "0.1.13"}
com.fluree/db {:git/url "https://github.com/fluree/db.git"
:sha "9e9718b11e954c47621ea2c4651105f6d0765535"}}
:git/sha "f0c10635d6a99e791d59676e7ed4191e82083486"}}

:aliases
{:dev
Expand Down
127 changes: 54 additions & 73 deletions src/fluree/http_api/components/http.clj
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,36 @@
[jsonista.core :as j]
[ring.adapter.jetty9 :as http]
[reitit.ring :as ring]
[reitit.coercion.spec]
[reitit.coercion.malli]
[reitit.swagger :as swagger]
[reitit.swagger-ui :as swagger-ui]
[reitit.ring.coercion :as coercion]
[reitit.ring.middleware.muuntaja :as muuntaja]
[reitit.ring.middleware.muuntaja :as muuntaja-mw]
[reitit.ring.middleware.exception :as exception]
[fluree.http-api.handlers.ledger :as ledger]
[clojure.spec.alpha :as s]
[muuntaja.core :as m]
[muuntaja.core :as muuntaja]
[muuntaja.format.json :as json-format]
[muuntaja.format.core :as mf]
[ring.middleware.cors :as rmc]
[fluree.db.util.log :as log])
(:import (java.io InputStream InputStreamReader)))
[fluree.db.util.log :as log]
[malli.core :as m]
[malli.experimental.lite :as l]
[fluree.db.query.history :refer [HistoryQuery]]
[fluree.db.query.fql.syntax :as fql]))

(set! *warn-on-reflection* true)

;; TODO: Flesh this out some more
(s/def ::non-empty-string (s/and string? #(< 0 (count %))))
(s/def ::address ::non-empty-string)
(s/def ::id ::non-empty-string)
(s/def ::t nat-int?)
(s/def ::alias ::non-empty-string)
(s/def ::action (s/or :keywords #{:new :insert}
:strings #{"new" "insert"}))
(s/def ::ledger ::non-empty-string)
(s/def ::txn (s/or :single-map map? :collection-of-maps (s/coll-of map?)))
(s/def ::context map?)
(def non-empty-string (m/schema [:string {:min 1}]))
(def address non-empty-string)
(def id non-empty-string)
(def natural-int (m/schema [:int {:min 0}]))
(def t natural-int)
(def ledger-alias non-empty-string)
(def txn (m/schema [:orn
[:single-map [:map-of :any :any]]
[:collection-of-maps [:sequential map?]]]))
(def context (m/schema [:map-of :any :any]))

(def server
#::ds{:start (fn [{{:keys [handler options]} ::ds/config}]
Expand All @@ -48,29 +50,31 @@

(def query-endpoint
{:summary "Endpoint for submitting queries"
:parameters {:body {:ledger string?
:query map?}}
:parameters {:body {:ledger ledger-alias
:query (m/schema ::fql/analytical-query
{:registry fql/registry})}}
:responses {200 {:body sequential?}
400 {:body string?}
500 {:body string?}}
400 {:body non-empty-string}
500 {:body non-empty-string}}
:handler ledger/query})

(def multi-query-endpoint
{:summary "Endpoint for submitting multi-queries"
:parameters {:body {:ledger string?
:query map?}}
:parameters {:body {:ledger ledger-alias
:query (m/schema ::fql/multi-query
{:registry fql/registry})}}
:responses {200 {:body map?}
400 {:body string?}
500 {:body string?}}
400 {:body non-empty-string}
500 {:body non-empty-string}}
:handler ledger/multi-query})

(def history-endpoint
{:summary "Endpoint for submitting history queries"
:parameters {:body {:ledger string?
:query map?}}
{:summary "Endpoint for submitting history queries"
:parameters {:body {:ledger ledger-alias
:query HistoryQuery}}
:responses {200 {:body sequential?}
400 {:body string?}
500 {:body string?}}
400 {:body non-empty-string}
500 {:body non-empty-string}}
:handler ledger/history})

(defn wrap-assoc-conn
Expand All @@ -97,33 +101,6 @@
[weighted-middleware]
(map (fn [[_ mw]] mw) (sort-by first weighted-middleware)))

(defn fluree-json-ld-decoder
[options]
(let [mapper (json-format/object-mapper! (assoc options
:decode-key-fn false))]
(reify
mf/Decode
(decode [_ data charset]
;; TODO: Surely there's a way to use the existing upstream decoder w/o
;; reflection? I couldn't figure it out so the code is copy-pasted
;; in the next five lines below.
(let [decoded (if (.equals "utf-8" ^String charset)
(j/read-value data mapper)
(j/read-value (InputStreamReader. ^InputStream data
^String charset)
mapper))]
;; keywordize only the top-level keys
(reduce-kv (fn [m k v]
(assoc m (keyword k) v))
{} decoded))))))

(def fluree-json-ld-format
(mf/map->Format
{:name "application/json"
:matches #"^application/(.+\+)?json$"
:encoder [json-format/encoder]
:decoder [fluree-json-ld-decoder]}))

(defn websocket-handler
[upgrade-request]
;; Mostly copy-pasta from
Expand Down Expand Up @@ -193,20 +170,28 @@
["/fluree" {:middleware fluree-middleware}
["/create"
{:post {:summary "Endpoint for creating new ledgers"
:parameters {:body (s/keys :opt-un [::context]
:req-un [::ledger ::txn])}
:responses {201 {:body (s/keys :opt-un [::address ::id]
:req-un [::alias ::t])}
400 {:body string?}
500 {:body string?}}
:parameters {:body [:map
[:ledger ledger-alias]
[:txn txn]
[:context {:optional true} context]]}
:responses {201 {:body [:map
[:alias ledger-alias]
[:t t]
[:address {:optional true} address]
[:id {:optional true} id]]}
400 {:body non-empty-string}
500 {:body non-empty-string}}
:handler ledger/create}}]
["/transact"
{:post {:summary "Endpoint for submitting transactions"
:parameters {:body (s/keys :req-un [::ledger ::txn])}
:responses {200 {:body (s/keys :opt-un [::address ::id]
:req-un [::alias ::t])}
400 {:body string?}
500 {:body string?}}
:parameters {:body {:ledger ledger-alias
:txn txn}}
:responses {200 {:body {:alias ledger-alias
:t t
:address (l/optional address)
:id (l/optional id)}}
400 {:body non-empty-string}
500 {:body non-empty-string}}
:handler ledger/transact}}]
["/query"
{:get query-endpoint
Expand All @@ -217,12 +202,8 @@
["/history"
{:get history-endpoint
:post history-endpoint}]]]
{:data {:coercion reitit.coercion.spec/coercion
:muuntaja (m/create
(assoc-in
m/default-options
[:formats "application/json"]
fluree-json-ld-format))
{:data {:coercion reitit.coercion.malli/coercion
:muuntaja muuntaja/instance
:middleware [swagger/swagger-feature
muuntaja/format-negotiate-middleware
muuntaja/format-response-middleware
Expand Down
18 changes: 9 additions & 9 deletions src/fluree/http_api/handlers/ledger.clj
Original file line number Diff line number Diff line change
Expand Up @@ -123,15 +123,15 @@

(def multi-query
(error-catching-handler
(fn [{:keys [fluree/conn] {{:keys [ledger query] :as body} :body} :parameters}]
(let [db (->> ledger (fluree/load conn) deref! fluree/db)
query* (-> (reduce-kv (fn [m k v]
(assoc m k (keywordize-keys v)))
{} query)
(assoc :opts (query-body->opts body)))]
(log/debug "multi-query - Querying ledger" ledger "-" query)
{:status 200
:body (deref! (fluree/multi-query db query*))}))))
(fn [{:keys [fluree/conn] {{:keys [ledger query] :as body} :body} :parameters}]
(let [db (->> ledger (fluree/load conn) deref! fluree/db)
query* (-> (reduce-kv (fn [m k v]
(assoc m k (keywordize-keys v)))
{} query)
(assoc :opts (query-body->opts body)))]
(log/debug "multi-query - Querying ledger" ledger "-" query)
{:status 200
:body (deref! (fluree/multi-query db query*))}))))


(def history
Expand Down

0 comments on commit c30889b

Please sign in to comment.