Compare commits
1 commit
master
...
reitit-fea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e5d10c084 |
20 changed files with 1134 additions and 0 deletions
8
deps.edn
8
deps.edn
|
|
@ -2,6 +2,7 @@
|
|||
"feature-yaml" "feature-csv" "feature-transit"
|
||||
"feature-java-time" "feature-java-nio"
|
||||
"feature-httpkit-client" "feature-httpkit-server"
|
||||
"feature-ring" "feature-reitit" "feature-selmer"
|
||||
"feature-lanterna"
|
||||
"sci/src" "babashka.curl/src" "pods/src"
|
||||
"babashka.nrepl/src"
|
||||
|
|
@ -20,6 +21,13 @@
|
|||
clj-commons/clj-yaml {:mvn/version "0.7.2"}
|
||||
com.cognitect/transit-clj {:mvn/version "1.0.324"}
|
||||
nrepl/bencode {:mvn/version "1.1.0"}
|
||||
ring/ring-core {:mvn/version "1.8.1"}
|
||||
ring/ring-defaults {:mvn/version "0.3.2"}
|
||||
ring-webjars {:mvn/version"0.2.0"}
|
||||
metosin/reitit {:mvn/version "0.5.5"}
|
||||
metosin/muuntaja {:mvn/version "0.6.7"}
|
||||
metosin/ring-http-response {:mvn/version "0.9.1"}
|
||||
selmer {:mvn/version "1.12.29"}
|
||||
seancorfield/next.jdbc {:mvn/version "1.1.610"}
|
||||
org.postgresql/postgresql {:mvn/version "42.2.18"}
|
||||
org.hsqldb/hsqldb {:mvn/version "2.5.1"}
|
||||
|
|
|
|||
10
feature-reitit/babashka/impl/muuntaja_core.clj
Normal file
10
feature-reitit/babashka/impl/muuntaja_core.clj
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
(ns babashka.impl.muuntaja-core
|
||||
(:require [muuntaja.core :as m]
|
||||
[sci.core :as sci :refer [copy-var]]))
|
||||
|
||||
(def mns (sci/create-ns 'muuntaja.core nil))
|
||||
|
||||
(def muuntaja-core-namespace
|
||||
{:obj mns
|
||||
'create (copy-var m/create mns)
|
||||
'default-options (copy-var m/default-options mns)})
|
||||
10
feature-reitit/babashka/impl/muuntaja_middleware.clj
Normal file
10
feature-reitit/babashka/impl/muuntaja_middleware.clj
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
(ns babashka.impl.muuntaja-middleware
|
||||
(:require [muuntaja.middleware :as m]
|
||||
[sci.core :as sci :refer [copy-var]]))
|
||||
|
||||
(def mns (sci/create-ns 'muuntaja.middleware nil))
|
||||
|
||||
(def muuntaja-middleware-namespace
|
||||
{:obj mns
|
||||
'wrap-format (copy-var m/wrap-format mns)
|
||||
'wrap-params (copy-var m/wrap-params mns)})
|
||||
13
feature-reitit/babashka/impl/reitit_ring.clj
Normal file
13
feature-reitit/babashka/impl/reitit_ring.clj
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
(ns babashka.impl.reitit-ring
|
||||
(:require [reitit.ring :as ring]
|
||||
[sci.core :as sci :refer [copy-var]]))
|
||||
|
||||
(def rns (sci/create-ns 'reitit.ring nil))
|
||||
|
||||
(def reitit-ring-namespace
|
||||
{:obj rns
|
||||
'ring-handler (copy-var ring/ring-handler rns)
|
||||
'router (copy-var ring/router rns)
|
||||
'routes (copy-var ring/routes rns)
|
||||
'create-resource-handler (copy-var ring/create-resource-handler rns)
|
||||
'create-default-handler (copy-var ring/create-default-handler rns)})
|
||||
452
feature-reitit/reitit/trie.cljc
Normal file
452
feature-reitit/reitit/trie.cljc
Normal file
|
|
@ -0,0 +1,452 @@
|
|||
(ns reitit.trie
|
||||
(:refer-clojure :exclude [compile])
|
||||
(:require [clojure.string :as str]
|
||||
[reitit.exception :as ex])
|
||||
#?(:clj (:import [reitit Trie Trie$Match Trie$Matcher]
|
||||
(java.net URLDecoder))))
|
||||
|
||||
(defn ^:no-doc into-set [x]
|
||||
(cond
|
||||
(or (set? x) (sequential? x)) (set x)
|
||||
(nil? x) #{}
|
||||
:else (conj #{} x)))
|
||||
|
||||
(defrecord Wild [value])
|
||||
(defrecord CatchAll [value])
|
||||
(defrecord Match [params data])
|
||||
(defrecord Node [children wilds catch-all params data])
|
||||
|
||||
(defn wild? [x] (instance? Wild x))
|
||||
(defn catch-all? [x] (instance? CatchAll x))
|
||||
|
||||
(defprotocol Matcher
|
||||
(match [this i max path])
|
||||
(view [this])
|
||||
(depth ^long [this])
|
||||
(length [this]))
|
||||
|
||||
(defprotocol TrieCompiler
|
||||
(data-matcher [this params data])
|
||||
(static-matcher [this path matcher])
|
||||
(wild-matcher [this key end matcher])
|
||||
(catch-all-matcher [this key params data])
|
||||
(linear-matcher [this matchers ordered?])
|
||||
(-pretty [this matcher])
|
||||
(-path-matcher [this matcher]))
|
||||
|
||||
(defn- assoc-param [match k v]
|
||||
(let [params (:params match)]
|
||||
(assoc match :params (assoc params k v))))
|
||||
|
||||
;; https://stackoverflow.com/questions/8033655/find-longest-common-prefix
|
||||
(defn- common-prefix [s1 s2]
|
||||
(let [max (min (count s1) (count s2))]
|
||||
(loop [i 0]
|
||||
(cond
|
||||
;; full match
|
||||
(> i max)
|
||||
(subs s1 0 max)
|
||||
;; partial match
|
||||
(not= (get s1 i) (get s2 i))
|
||||
(if-not (zero? i) (subs s1 0 i))
|
||||
;; recur
|
||||
:else (recur (inc i))))))
|
||||
|
||||
(defn- -keyword [s]
|
||||
(if-let [^long i (str/index-of s "/")]
|
||||
(keyword (subs s 0 i) (subs s (inc i)))
|
||||
(keyword s)))
|
||||
|
||||
(defn split-path [s {:keys [syntax] :or {syntax #{:bracket :colon}}}]
|
||||
(let [bracket? (-> syntax (into-set) :bracket)
|
||||
colon? (-> syntax (into-set) :colon)
|
||||
-static (fn [from to] (if-not (= from to) [(subs s from to)]))
|
||||
-wild (fn [^long from to] [(->Wild (-keyword (subs s (inc from) to)))])
|
||||
-catch-all (fn [^long from to] [(->CatchAll (keyword (subs s (inc from) to)))])]
|
||||
(loop [ss nil, from 0, to 0]
|
||||
(if (= to (count s))
|
||||
(concat ss (-static from to))
|
||||
(let [c (get s to)]
|
||||
(cond
|
||||
|
||||
(and bracket? (= \{ c))
|
||||
(let [^long to' (or (str/index-of s "}" to) (ex/fail! ::unclosed-brackets {:path s}))]
|
||||
(if (= \* (get s (inc to)))
|
||||
(recur (concat ss (-static from to) (-catch-all (inc to) to')) (long (inc to')) (long (inc to')))
|
||||
(recur (concat ss (-static from to) (-wild to to')) (long (inc to')) (long (inc to')))))
|
||||
|
||||
(and colon? (= \: c))
|
||||
(let [^long to' (or (str/index-of s "/" to) (count s))]
|
||||
(if (= 1 (- to' to))
|
||||
(recur ss from (inc to))
|
||||
(recur (concat ss (-static from to) (-wild to to')) (long to') (long to'))))
|
||||
|
||||
(and colon? (= \* c))
|
||||
(let [to' (count s)]
|
||||
(recur (concat ss (-static from to) (-catch-all to to')) (long to') (long to')))
|
||||
|
||||
:else
|
||||
(recur ss from (inc to))))))))
|
||||
|
||||
(defn join-path [xs]
|
||||
(reduce
|
||||
(fn [s x]
|
||||
(str s (cond
|
||||
(string? x) x
|
||||
(instance? Wild x) (str "{" (-> x :value str (subs 1)) "}")
|
||||
(instance? CatchAll x) (str "{*" (-> x :value str (subs 1)) "}"))))
|
||||
"" xs))
|
||||
|
||||
(defn normalize [s opts]
|
||||
(-> s (split-path opts) (join-path)))
|
||||
|
||||
;;
|
||||
;; Conflict Resolution
|
||||
;;
|
||||
|
||||
(defn- -slice-start [[p1 :as p1s] [p2 :as p2s]]
|
||||
(let [-split (fn [p]
|
||||
(if-let [i (and p (str/index-of p "/"))]
|
||||
[(subs p 0 i) (subs p i)]
|
||||
[p]))
|
||||
-slash (fn [cp p]
|
||||
(cond
|
||||
(not (string? cp)) [cp]
|
||||
(and (string? cp) (not= (count cp) (count p))) [(subs p (count cp))]
|
||||
(and (string? p) (not cp)) (-split p)))
|
||||
-postcut (fn [[p :as pps]]
|
||||
(let [^long i (and p (str/index-of p "/"))]
|
||||
(if (and i (pos? i))
|
||||
(concat [(subs p 0 i) (subs p i)] (rest pps))
|
||||
pps)))
|
||||
-tailcut (fn [cp [p :as ps]] (concat (-slash cp p) (rest ps)))]
|
||||
(if (or (nil? p1) (nil? p2))
|
||||
[(-postcut p1s) (-postcut p2s)]
|
||||
(if-let [cp (and (string? p1) (string? p2) (common-prefix p1 p2))]
|
||||
[(-tailcut cp p1s) (-tailcut cp p2s)]
|
||||
[p1s p2s]))))
|
||||
|
||||
(defn- -slice-end [x xs]
|
||||
(let [i (if (string? x) (str/index-of x "/"))]
|
||||
(if (and (number? i) (pos? ^long i))
|
||||
(concat [(subs x i)] xs)
|
||||
xs)))
|
||||
|
||||
(defn conflicting-parts? [parts1 parts2]
|
||||
(let [[[s1 & ss1] [s2 & ss2]] (-slice-start parts1 parts2)]
|
||||
(cond
|
||||
(= s1 s2 nil) true
|
||||
(or (nil? s1) (nil? s2)) false
|
||||
(or (catch-all? s1) (catch-all? s2)) true
|
||||
(or (wild? s1) (wild? s2)) (recur (-slice-end s1 ss1) (-slice-end s2 ss2))
|
||||
(not= s1 s2) false
|
||||
:else (recur ss1 ss2))))
|
||||
|
||||
(defn conflicting-paths? [path1 path2 opts]
|
||||
(conflicting-parts? (split-path path1 opts) (split-path path2 opts)))
|
||||
|
||||
;;
|
||||
;; Creating Tries
|
||||
;;
|
||||
|
||||
(defn- -node [m]
|
||||
(map->Node (merge {:children {}, :wilds {}, :catch-all {}, :params {}} m)))
|
||||
|
||||
(defn- -insert [node [path & ps] fp params data]
|
||||
(let [node' (cond
|
||||
|
||||
(nil? path)
|
||||
(assoc node :data data :params params)
|
||||
|
||||
(instance? Wild path)
|
||||
(let [next (first ps)]
|
||||
(if (or (instance? Wild next) (instance? CatchAll next))
|
||||
(ex/fail! ::following-parameters {:path fp, :parameters (map :value [path next])})
|
||||
(update-in node [:wilds path] (fn [n] (-insert (or n (-node {})) ps fp params data)))))
|
||||
|
||||
(instance? CatchAll path)
|
||||
(assoc-in node [:catch-all path] (-node {:params params, :data data}))
|
||||
|
||||
(str/blank? path)
|
||||
(-insert node ps fp params data)
|
||||
|
||||
:else
|
||||
(or
|
||||
(reduce
|
||||
(fn [_ [p n]]
|
||||
(if-let [cp (common-prefix p path)]
|
||||
(if (= cp p)
|
||||
;; insert into child node
|
||||
(let [n' (-insert n (conj ps (subs path (count p))) fp params data)]
|
||||
(reduced (assoc-in node [:children p] n')))
|
||||
;; split child node
|
||||
(let [rp (subs p (count cp))
|
||||
rp' (subs path (count cp))
|
||||
n' (-insert (-node {}) ps fp params data)
|
||||
n'' (-insert (-node {:children {rp n, rp' n'}}) nil nil nil nil)]
|
||||
(reduced (update node :children (fn [children]
|
||||
(-> children
|
||||
(dissoc p)
|
||||
(assoc cp n'')))))))))
|
||||
nil (:children node))
|
||||
;; new child node
|
||||
(assoc-in node [:children path] (-insert (-node {}) ps fp params data))))]
|
||||
(if-let [child (get-in node' [:children ""])]
|
||||
;; optimize by removing empty paths
|
||||
(-> (merge-with merge (dissoc node' :data) child)
|
||||
(update :children dissoc ""))
|
||||
node')))
|
||||
|
||||
(defn- decode [path start end percent?]
|
||||
(let [param (subs path start end)]
|
||||
(if percent?
|
||||
#?(:cljs (js/decodeURIComponent param)
|
||||
:clj (URLDecoder/decode
|
||||
(if (.contains ^String param "+")
|
||||
(.replace ^String param "+" "%2B")
|
||||
param)
|
||||
"UTF-8"))
|
||||
param)))
|
||||
|
||||
;;
|
||||
;; Compilers
|
||||
;;
|
||||
|
||||
(defn clojure-trie-compiler []
|
||||
(reify
|
||||
TrieCompiler
|
||||
(data-matcher [_ params data]
|
||||
(let [match (->Match params data)]
|
||||
(reify Matcher
|
||||
(match [_ i max _]
|
||||
(if (= i max)
|
||||
match))
|
||||
(view [_] data)
|
||||
(depth [_] 1)
|
||||
(length [_]))))
|
||||
(static-matcher [_ path matcher]
|
||||
(let [size (count path)]
|
||||
(reify Matcher
|
||||
(match [_ i max p]
|
||||
(if-not (< ^long max (+ ^long i size))
|
||||
(loop [j 0]
|
||||
(if (= j size)
|
||||
(match matcher (+ ^long i size) max p)
|
||||
(if (= (get p (+ ^long i j)) (get path j))
|
||||
(recur (inc j)))))))
|
||||
(view [_] [path (view matcher)])
|
||||
(depth [_] (inc (depth matcher)))
|
||||
(length [_] (count path)))))
|
||||
(wild-matcher [_ key end matcher]
|
||||
(reify Matcher
|
||||
(match [_ i max path]
|
||||
(if (and (< ^long i ^long max) (not= (get path i) end))
|
||||
(loop [percent? false, j ^long i]
|
||||
(if (= max j)
|
||||
(if-let [match (match matcher max max path)]
|
||||
(assoc-param match key (decode path i max percent?)))
|
||||
(let [c ^char (get path j)]
|
||||
(condp = c
|
||||
end (if-let [match (match matcher j max path)]
|
||||
(assoc-param match key (decode path i j percent?)))
|
||||
\% (recur true (inc j))
|
||||
(recur percent? (inc j))))))))
|
||||
(view [_] [key (view matcher)])
|
||||
(depth [_] (inc (depth matcher)))
|
||||
(length [_])))
|
||||
(catch-all-matcher [_ key params data]
|
||||
(let [match (->Match params data)]
|
||||
(reify Matcher
|
||||
(match [_ i max path]
|
||||
(if (<= ^long i ^long max) (assoc-param match key (decode path i max true))))
|
||||
(view [_] [key [data]])
|
||||
(depth [_] 1)
|
||||
(length [_]))))
|
||||
(linear-matcher [_ matchers ordered?]
|
||||
(let [matchers (vec (if ordered? matchers (reverse (sort-by (juxt depth length) matchers))))
|
||||
size (count matchers)]
|
||||
(reify Matcher
|
||||
(match [_ i max path]
|
||||
(loop [j 0]
|
||||
(if (< j size)
|
||||
(or (match (get matchers j) i max path)
|
||||
(recur (inc j))))))
|
||||
(view [_] (mapv view matchers))
|
||||
(depth [_] (inc ^long (apply max 0 (map depth matchers))))
|
||||
(length [_]))))
|
||||
(-pretty [_ matcher]
|
||||
(view matcher))
|
||||
(-path-matcher [_ matcher]
|
||||
(fn [path]
|
||||
(if-let [match (match matcher 0 (count path) path)]
|
||||
(->Match (:params match) (:data match)))))))
|
||||
|
||||
#?(:clj
|
||||
(defn java-trie-compiler []
|
||||
(reify
|
||||
TrieCompiler
|
||||
(data-matcher [_ params data]
|
||||
(Trie/dataMatcher params data))
|
||||
(static-matcher [_ path matcher]
|
||||
(Trie/staticMatcher ^String path ^Trie$Matcher matcher))
|
||||
(wild-matcher [_ key end matcher]
|
||||
(Trie/wildMatcher key (if end (Character. end)) matcher))
|
||||
(catch-all-matcher [_ key params data]
|
||||
(Trie/catchAllMatcher key params data))
|
||||
(linear-matcher [_ matchers ordered?]
|
||||
(Trie/linearMatcher matchers ordered?))
|
||||
(-pretty [_ matcher]
|
||||
(-> matcher str read-string eval))
|
||||
(-path-matcher [_ matcher]
|
||||
(fn [path]
|
||||
(if-let [match ^Trie$Match (Trie/lookup ^Trie$Matcher matcher ^String path)]
|
||||
(->Match (.params match) (.data match))))))))
|
||||
|
||||
;;
|
||||
;; Managing Tries
|
||||
;;
|
||||
|
||||
(defn- map-parameters [keys]
|
||||
(zipmap keys (repeat nil)))
|
||||
|
||||
#?(:clj
|
||||
(def record-parameters
|
||||
"Memoized function to transform parameters into runtime generated Record."
|
||||
(memoize
|
||||
(fn [keys]
|
||||
(if (some qualified-keyword? keys)
|
||||
(map-parameters keys)
|
||||
(let [sym (gensym "PathParams")
|
||||
ctor (symbol (str "map->" sym))]
|
||||
(binding [*ns* (find-ns 'user)]
|
||||
(eval `(do (defrecord ~sym ~(mapv (comp symbol name) keys)) (~ctor {}))))))))))
|
||||
|
||||
(defn insert
|
||||
"Returns a trie with routes added to it."
|
||||
([routes]
|
||||
(insert nil routes))
|
||||
([node routes]
|
||||
(reduce
|
||||
(fn [acc [p d]]
|
||||
(insert acc p d))
|
||||
node routes))
|
||||
([node path data]
|
||||
(insert node path data nil))
|
||||
([node path data {::keys [parameters] :or {parameters map-parameters} :as opts}]
|
||||
(let [parts (split-path path opts)
|
||||
params (parameters (->> parts (remove string?) (map :value)))]
|
||||
(-insert (or node (-node {})) (split-path path opts) path params data))))
|
||||
|
||||
(defn babashka-message [& _]
|
||||
(println "Reitit compiler not supported by Babashka"))
|
||||
|
||||
(def compiler babashka-message)
|
||||
|
||||
(def compile babashka-message)
|
||||
|
||||
#_(defn compiler
|
||||
"Returns a default [[TrieCompiler]]."
|
||||
[]
|
||||
#?(:cljs (clojure-trie-compiler)
|
||||
:clj (java-trie-compiler)))
|
||||
|
||||
#_(defn compile
|
||||
"Returns a compiled trie, to be used with [[pretty]] or [[path-matcher]]."
|
||||
([options]
|
||||
(compile options (compiler)))
|
||||
([options compiler]
|
||||
(compile options compiler []))
|
||||
([{:keys [data params children wilds catch-all] :or {params {}}} compiler cp]
|
||||
(let [ends (fn [{:keys [children]}] (or (keys children) ["/"]))
|
||||
matchers (-> []
|
||||
(cond-> data (conj (data-matcher compiler params data)))
|
||||
(into (for [[p c] children] (static-matcher compiler p (compile c compiler (conj cp p)))))
|
||||
(into
|
||||
(for [[p c] wilds]
|
||||
(let [pv (:value p)
|
||||
ends (ends c)]
|
||||
(if (next ends)
|
||||
(ex/fail! ::multiple-terminators {:terminators ends, :path (join-path (conj cp p))})
|
||||
(wild-matcher compiler pv (ffirst ends) (compile c compiler (conj cp pv)))))))
|
||||
(into (for [[p c] catch-all] (catch-all-matcher compiler (:value p) params (:data c)))))]
|
||||
(cond
|
||||
(> (count matchers) 1) (linear-matcher compiler matchers false)
|
||||
(= (count matchers) 1) (first matchers)
|
||||
:else (data-matcher compiler {} nil)))))
|
||||
|
||||
(defn pretty
|
||||
"Returns a simplified EDN structure of a compiled trie for printing purposes."
|
||||
([compiled-trie]
|
||||
(pretty compiled-trie (compiler)))
|
||||
([compiled-trie compiler]
|
||||
(-pretty compiler compiled-trie)))
|
||||
|
||||
(defn path-matcher
|
||||
"Returns a function of `path -> Match` from a compiled trie."
|
||||
([compiled-trie]
|
||||
(path-matcher compiled-trie (compiler)))
|
||||
([compiled-trie compiler]
|
||||
(-path-matcher compiler compiled-trie)))
|
||||
|
||||
;;
|
||||
;; spike
|
||||
;;
|
||||
|
||||
(comment
|
||||
(->
|
||||
[["/v2/whoami" 1]
|
||||
["/v2/users/:user-id/datasets" 2]
|
||||
["/v2/public/projects/:project-id/datasets" 3]
|
||||
["/v1/public/topics/:topic" 4]
|
||||
["/v1/users/:user-id/orgs/:org-id" 5]
|
||||
["/v1/search/topics/:term" 6]
|
||||
["/v1/users/:user-id/invitations" 7]
|
||||
["/v1/users/:user-id/topics" 9]
|
||||
["/v1/users/:user-id/bookmarks/followers" 10]
|
||||
["/v2/datasets/:dataset-id" 11]
|
||||
["/v1/orgs/:org-id/usage-stats" 12]
|
||||
["/v1/orgs/:org-id/devices/:client-id" 13]
|
||||
["/v1/messages/user/:user-id" 14]
|
||||
["/v1/users/:user-id/devices" 15]
|
||||
["/v1/public/users/:user-id" 16]
|
||||
["/v1/orgs/:org-id/errors" 17]
|
||||
["/v1/public/orgs/:org-id" 18]
|
||||
["/v1/orgs/:org-id/invitations" 19]
|
||||
["/v1/users/:user-id/device-errors" 22]
|
||||
["/v2/login" 23]
|
||||
["/v1/users/:user-id/usage-stats" 24]
|
||||
["/v2/users/:user-id/devices" 25]
|
||||
["/v1/users/:user-id/claim-device/:client-id" 26]
|
||||
["/v2/public/projects/:project-id" 27]
|
||||
["/v2/public/datasets/:dataset-id" 28]
|
||||
["/v2/users/:user-id/topics/bulk" 29]
|
||||
["/v1/messages/device/:client-id" 30]
|
||||
["/v1/users/:user-id/owned-orgs" 31]
|
||||
["/v1/topics/:topic" 32]
|
||||
["/v1/users/:user-id/bookmark/:topic" 33]
|
||||
["/v1/orgs/:org-id/members/:user-id" 34]
|
||||
["/v1/users/:user-id/devices/:client-id" 35]
|
||||
["/v1/users/:user-id" 36]
|
||||
["/v1/orgs/:org-id/devices" 37]
|
||||
["/v1/orgs/:org-id/members" 38]
|
||||
["/v2/orgs/:org-id/topics" 40]
|
||||
["/v1/whoami" 41]
|
||||
["/v1/orgs/:org-id" 42]
|
||||
["/v1/users/:user-id/api-key" 43]
|
||||
["/v2/schemas" 44]
|
||||
["/v2/users/:user-id/topics" 45]
|
||||
["/v1/orgs/:org-id/confirm-membership/:token" 46]
|
||||
["/v2/topics/:topic" 47]
|
||||
["/v1/messages/topic/:topic" 48]
|
||||
["/v1/users/:user-id/devices/:client-id/reset-password" 49]
|
||||
["/v2/topics" 50]
|
||||
["/v1/login" 51]
|
||||
["/v1/users/:user-id/orgs" 52]
|
||||
["/v2/public/messages/dataset/:dataset-id" 53]
|
||||
["/v1/topics" 54]
|
||||
["/v1/orgs" 55]
|
||||
["/v1/users/:user-id/bookmarks" 56]
|
||||
["/v1/orgs/:org-id/topics" 57]]
|
||||
(insert)
|
||||
(compile)
|
||||
(pretty)))
|
||||
13
feature-ring/babashka/impl/ring_middleware_anti_forgery.clj
Normal file
13
feature-ring/babashka/impl/ring_middleware_anti_forgery.clj
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
(ns babashka.impl.ring-middleware-anti-forgery
|
||||
(:require [ring.middleware.anti-forgery :as anti-forgery]
|
||||
[sci.core :as sci :refer [copy-var]]))
|
||||
|
||||
(def ans (sci/create-ns 'ring.middleware.anti-forgery nil))
|
||||
|
||||
(defn get-anti-forgery-token []
|
||||
anti-forgery/*anti-forgery-token*)
|
||||
|
||||
(def ring-middleware-anti-forgery-namespace
|
||||
{:obj ans
|
||||
'wrap-anti-forgery (copy-var anti-forgery/wrap-anti-forgery ans)
|
||||
'get-anti-forgery-token (copy-var get-anti-forgery-token ans)})
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
(ns babashka.impl.ring-middleware-content-type
|
||||
(:require [ring.middleware.content-type :as content-type]
|
||||
[sci.core :as sci :refer [copy-var]]))
|
||||
|
||||
(def cns (sci/create-ns 'ring.middleware.content-type nil))
|
||||
|
||||
(def ring-middleware-content-type-namespace
|
||||
{:obj cns
|
||||
'wrap-content-type (copy-var content-type/wrap-content-type cns)})
|
||||
15
feature-ring/babashka/impl/ring_middleware_defaults.clj
Normal file
15
feature-ring/babashka/impl/ring_middleware_defaults.clj
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
(ns babashka.impl.ring-middleware-defaults
|
||||
(:require [ring.middleware.defaults :as defaults]
|
||||
[ring.middleware.multipart-params]
|
||||
[ring.middleware.multipart-params.temp-file]
|
||||
[sci.core :as sci :refer [copy-var]]))
|
||||
|
||||
(alter-var-root #'ring.middleware.multipart-params/default-store (constantly (delay ring.middleware.multipart-params.temp-file/temp-file-store)))
|
||||
|
||||
(def dns (sci/create-ns 'ring.middleware.defaults nil))
|
||||
|
||||
(def ring-middleware-defaults-namespace
|
||||
{:obj dns
|
||||
'wrap-defaults (copy-var defaults/wrap-defaults dns)
|
||||
'api-defaults (copy-var defaults/api-defaults dns)
|
||||
'site-defaults (copy-var defaults/site-defaults dns)})
|
||||
9
feature-ring/babashka/impl/ring_middleware_webjars.clj
Normal file
9
feature-ring/babashka/impl/ring_middleware_webjars.clj
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
(ns babashka.impl.ring-middleware-webjars
|
||||
(:require [ring.middleware.webjars :as webjars]
|
||||
[sci.core :as sci :refer [copy-var]]))
|
||||
|
||||
(def wns (sci/create-ns 'ring.middleware.webjars nil))
|
||||
|
||||
(def ring-middleware-webjars-namespace
|
||||
{:obj wns
|
||||
'wrap-webjars (copy-var webjars/wrap-webjars wns)})
|
||||
12
feature-ring/babashka/impl/ring_util_http_response.clj
Normal file
12
feature-ring/babashka/impl/ring_util_http_response.clj
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
(ns babashka.impl.ring-util-http-response
|
||||
(:require [ring.util.http-response :as http-response]
|
||||
[sci.core :as sci :refer [copy-var]]))
|
||||
|
||||
(def hns (sci/create-ns 'ring.util.http-response nil))
|
||||
|
||||
(def ring-util-http-response-namespace
|
||||
{:obj hns
|
||||
'ok (copy-var http-response/ok hns)
|
||||
'content-type (copy-var http-response/content-type hns)
|
||||
'bad-request (copy-var http-response/bad-request hns)
|
||||
'internal-server-error (copy-var http-response/internal-server-error hns)})
|
||||
9
feature-ring/babashka/impl/ring_util_request.clj
Normal file
9
feature-ring/babashka/impl/ring_util_request.clj
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
(ns babashka.impl.ring-util-request
|
||||
(:require [ring.util.request :as request]
|
||||
[sci.core :as sci :refer [copy-var]]))
|
||||
|
||||
(def rns (sci/create-ns 'ring.util.request nil))
|
||||
|
||||
(def ring-util-request-namespace
|
||||
{:obj rns
|
||||
'body-string (copy-var request/body-string rns)})
|
||||
11
feature-ring/babashka/impl/ring_util_response.clj
Normal file
11
feature-ring/babashka/impl/ring_util_response.clj
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
(ns babashka.impl.ring-util-response
|
||||
(:require [ring.util.response :as response]
|
||||
[sci.core :as sci :refer [copy-var]]))
|
||||
|
||||
(def rns (sci/create-ns 'ring.util.response nil))
|
||||
|
||||
(def ring-util-response-namespace
|
||||
{:obj rns
|
||||
'response (copy-var response/response rns)
|
||||
'resource-data (copy-var response/resource-data rns)
|
||||
'header (copy-var response/header rns)})
|
||||
117
feature-ring/ring/middleware/defaults.clj
Normal file
117
feature-ring/ring/middleware/defaults.clj
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
(ns ring.middleware.defaults
|
||||
"Middleware for providing a handler with sensible defaults."
|
||||
(:require [ring.middleware.x-headers :as x]
|
||||
[ring.middleware.flash :refer [wrap-flash]]
|
||||
[ring.middleware.session :refer [wrap-session]]
|
||||
[ring.middleware.keyword-params :refer [wrap-keyword-params]]
|
||||
[ring.middleware.nested-params :refer [wrap-nested-params]]
|
||||
[ring.middleware.anti-forgery :refer [wrap-anti-forgery]]
|
||||
[ring.middleware.multipart-params :refer [wrap-multipart-params]]
|
||||
[ring.middleware.params :refer [wrap-params]]
|
||||
[ring.middleware.cookies :refer [wrap-cookies]]
|
||||
[ring.middleware.resource :refer [wrap-resource]]
|
||||
[ring.middleware.file :refer [wrap-file]]
|
||||
[ring.middleware.not-modified :refer [wrap-not-modified]]
|
||||
[ring.middleware.content-type :refer [wrap-content-type]]
|
||||
[ring.middleware.default-charset :refer [wrap-default-charset]]
|
||||
[ring.middleware.absolute-redirects :refer [wrap-absolute-redirects]]
|
||||
[ring.middleware.ssl :refer [wrap-ssl-redirect wrap-hsts wrap-forwarded-scheme]]
|
||||
[ring.middleware.proxy-headers :refer [wrap-forwarded-remote-addr]]))
|
||||
|
||||
(def api-defaults
|
||||
"A default configuration for a HTTP API."
|
||||
{:params {:urlencoded true
|
||||
:keywordize true}
|
||||
:responses {:not-modified-responses true
|
||||
:absolute-redirects true
|
||||
:content-types true
|
||||
:default-charset "utf-8"}})
|
||||
|
||||
(def secure-api-defaults
|
||||
"A default configuration for a HTTP API that's accessed securely over HTTPS."
|
||||
(-> api-defaults
|
||||
(assoc-in [:security :ssl-redirect] true)
|
||||
(assoc-in [:security :hsts] true)))
|
||||
|
||||
(def site-defaults
|
||||
"A default configuration for a browser-accessible website, based on current
|
||||
best practice."
|
||||
{:params {:urlencoded true
|
||||
:multipart true
|
||||
:nested true
|
||||
:keywordize true}
|
||||
:cookies true
|
||||
:session {:flash true
|
||||
:cookie-attrs {:http-only true, :same-site :strict}}
|
||||
:security {:anti-forgery true
|
||||
:xss-protection {:enable? true, :mode :block}
|
||||
:frame-options :sameorigin
|
||||
:content-type-options :nosniff}
|
||||
:static {:resources "public"}
|
||||
:responses {:not-modified-responses true
|
||||
:absolute-redirects true
|
||||
:content-types true
|
||||
:default-charset "utf-8"}})
|
||||
|
||||
(def secure-site-defaults
|
||||
"A default configuration for a browser-accessible website that's accessed
|
||||
securely over HTTPS."
|
||||
(-> site-defaults
|
||||
(assoc-in [:session :cookie-attrs :secure] true)
|
||||
(assoc-in [:session :cookie-name] "secure-ring-session")
|
||||
(assoc-in [:security :ssl-redirect] true)
|
||||
(assoc-in [:security :hsts] true)))
|
||||
|
||||
(defn- wrap [handler middleware options]
|
||||
(if (true? options)
|
||||
(middleware handler)
|
||||
(if options
|
||||
(middleware handler options)
|
||||
handler)))
|
||||
|
||||
(defn- wrap-multi [handler middleware args]
|
||||
(wrap handler
|
||||
(fn [handler args]
|
||||
(if (coll? args)
|
||||
(reduce middleware handler args)
|
||||
(middleware handler args)))
|
||||
args))
|
||||
|
||||
(defn- wrap-xss-protection [handler options]
|
||||
(x/wrap-xss-protection handler (:enable? options true) (dissoc options :enable?)))
|
||||
|
||||
(defn- wrap-x-headers [handler options]
|
||||
(-> handler
|
||||
(wrap wrap-xss-protection (:xss-protection options false))
|
||||
(wrap x/wrap-frame-options (:frame-options options false))
|
||||
(wrap x/wrap-content-type-options (:content-type-options options false))))
|
||||
|
||||
(defn wrap-defaults
|
||||
"Wraps a handler in default Ring middleware, as specified by the supplied
|
||||
configuration map.
|
||||
|
||||
See: api-defaults
|
||||
site-defaults
|
||||
secure-api-defaults
|
||||
secure-site-defaults"
|
||||
[handler config]
|
||||
(-> handler
|
||||
(wrap wrap-anti-forgery (get-in config [:security :anti-forgery] false))
|
||||
(wrap wrap-flash (get-in config [:session :flash] false))
|
||||
(wrap wrap-session (:session config false))
|
||||
(wrap wrap-keyword-params (get-in config [:params :keywordize] false))
|
||||
(wrap wrap-nested-params (get-in config [:params :nested] false))
|
||||
(wrap wrap-multipart-params (get-in config [:params :multipart] false))
|
||||
(wrap wrap-params (get-in config [:params :urlencoded] false))
|
||||
(wrap wrap-cookies (get-in config [:cookies] false))
|
||||
(wrap wrap-absolute-redirects (get-in config [:responses :absolute-redirects] false))
|
||||
(wrap-multi #(wrap-resource %1 %2 config) (get-in config [:static :resources] false))
|
||||
(wrap-multi wrap-file (get-in config [:static :files] false))
|
||||
(wrap wrap-content-type (get-in config [:responses :content-types] false))
|
||||
(wrap wrap-default-charset (get-in config [:responses :default-charset] false))
|
||||
(wrap wrap-not-modified (get-in config [:responses :not-modified-responses] false))
|
||||
(wrap wrap-x-headers (:security config))
|
||||
(wrap wrap-hsts (get-in config [:security :hsts] false))
|
||||
(wrap wrap-ssl-redirect (get-in config [:security :ssl-redirect] false))
|
||||
(wrap wrap-forwarded-scheme (boolean (:proxy config)))
|
||||
(wrap wrap-forwarded-remote-addr (boolean (:proxy config)))))
|
||||
348
feature-ring/ring/util/response.clj
Normal file
348
feature-ring/ring/util/response.clj
Normal file
|
|
@ -0,0 +1,348 @@
|
|||
(ns ring.util.response
|
||||
"Functions for generating and augmenting response maps."
|
||||
(:require [clojure.java.io :as io]
|
||||
[clojure.string :as str]
|
||||
[ring.util.io :refer [last-modified-date]]
|
||||
[ring.util.parsing :as parsing]
|
||||
[ring.util.time :refer [format-date]])
|
||||
(:import [java.io File]
|
||||
[java.util Date]
|
||||
[java.net URL URLDecoder URLEncoder]))
|
||||
|
||||
(def ^{:added "1.4"} redirect-status-codes
|
||||
"Map a keyword to a redirect status code."
|
||||
{:moved-permanently 301
|
||||
:found 302
|
||||
:see-other 303
|
||||
:temporary-redirect 307
|
||||
:permanent-redirect 308})
|
||||
|
||||
(defn redirect
|
||||
"Returns a Ring response for an HTTP 302 redirect. Status may be
|
||||
a key in redirect-status-codes or a numeric code. Defaults to 302"
|
||||
([url] (redirect url :found))
|
||||
([url status]
|
||||
{:status (redirect-status-codes status status)
|
||||
:headers {"Location" url}
|
||||
:body ""}))
|
||||
|
||||
(defn redirect-after-post
|
||||
"Returns a Ring response for an HTTP 303 redirect. Deprecated in favor
|
||||
of using redirect with a :see-other status."
|
||||
{:deprecated "1.4"}
|
||||
[url]
|
||||
{:status 303
|
||||
:headers {"Location" url}
|
||||
:body ""})
|
||||
|
||||
(defn created
|
||||
"Returns a Ring response for a HTTP 201 created response."
|
||||
{:added "1.2"}
|
||||
([url] (created url nil))
|
||||
([url body]
|
||||
{:status 201
|
||||
:headers {"Location" url}
|
||||
:body body}))
|
||||
|
||||
(defn bad-request
|
||||
"Returns a 400 'bad request' response."
|
||||
{:added "1.7"}
|
||||
[body]
|
||||
{:status 400
|
||||
:headers {}
|
||||
:body body})
|
||||
|
||||
(defn not-found
|
||||
"Returns a 404 'not found' response."
|
||||
{:added "1.1"}
|
||||
[body]
|
||||
{:status 404
|
||||
:headers {}
|
||||
:body body})
|
||||
|
||||
(defn response
|
||||
"Returns a skeletal Ring response with the given body, status of 200, and no
|
||||
headers."
|
||||
[body]
|
||||
{:status 200
|
||||
:headers {}
|
||||
:body body})
|
||||
|
||||
(defn status
|
||||
"Returns an updated Ring response with the given status."
|
||||
([status]
|
||||
{:status status
|
||||
:headers {}
|
||||
:body nil})
|
||||
([resp status]
|
||||
(assoc resp :status status)))
|
||||
|
||||
(defn header
|
||||
"Returns an updated Ring response with the specified header added."
|
||||
[resp name value]
|
||||
(assoc-in resp [:headers name] (str value)))
|
||||
|
||||
(defn- canonical-path ^String [^File file]
|
||||
(str (.getCanonicalPath file)
|
||||
(if (.isDirectory file) File/separatorChar)))
|
||||
|
||||
(defn- safe-path? [^String root ^String path]
|
||||
(.startsWith (canonical-path (File. root path))
|
||||
(canonical-path (File. root))))
|
||||
|
||||
(defn- directory-transversal?
|
||||
"Check if a path contains '..'."
|
||||
[^String path]
|
||||
(-> (str/split path #"/|\\")
|
||||
(set)
|
||||
(contains? "..")))
|
||||
|
||||
(defn- find-file-named [^File dir ^String filename]
|
||||
(let [path (File. dir filename)]
|
||||
(if (.isFile path)
|
||||
path)))
|
||||
|
||||
(defn- find-file-starting-with [^File dir ^String prefix]
|
||||
(first
|
||||
(filter
|
||||
#(.startsWith (.toLowerCase (.getName ^File %)) prefix)
|
||||
(.listFiles dir))))
|
||||
|
||||
(defn- find-index-file
|
||||
"Search the directory for an index file."
|
||||
[^File dir]
|
||||
(or (find-file-named dir "index.html")
|
||||
(find-file-named dir "index.htm")
|
||||
(find-file-starting-with dir "index.")))
|
||||
|
||||
(defn- safely-find-file [^String path opts]
|
||||
(if-let [^String root (:root opts)]
|
||||
(if (or (safe-path? root path)
|
||||
(and (:allow-symlinks? opts) (not (directory-transversal? path))))
|
||||
(File. root path))
|
||||
(File. path)))
|
||||
|
||||
(defn- find-file [^String path opts]
|
||||
(if-let [^File file (safely-find-file path opts)]
|
||||
(cond
|
||||
(.isDirectory file)
|
||||
(and (:index-files? opts true) (find-index-file file))
|
||||
(.exists file)
|
||||
file)))
|
||||
|
||||
(defn- file-data [^File file]
|
||||
{:content file
|
||||
:content-length (.length file)
|
||||
:last-modified (last-modified-date file)})
|
||||
|
||||
(defn- content-length [resp len]
|
||||
(if len
|
||||
(header resp "Content-Length" len)
|
||||
resp))
|
||||
|
||||
(defn- last-modified [resp last-mod]
|
||||
(if last-mod
|
||||
(header resp "Last-Modified" (format-date last-mod))
|
||||
resp))
|
||||
|
||||
(defn file-response
|
||||
"Returns a Ring response to serve a static file, or nil if an appropriate
|
||||
file does not exist.
|
||||
Options:
|
||||
:root - take the filepath relative to this root path
|
||||
:index-files? - look for index.* files in directories (defaults to true)
|
||||
:allow-symlinks? - allow symlinks that lead to paths outside the root path
|
||||
(defaults to false)"
|
||||
([filepath]
|
||||
(file-response filepath {}))
|
||||
([filepath options]
|
||||
(if-let [file (find-file filepath options)]
|
||||
(let [data (file-data file)]
|
||||
(-> (response (:content data))
|
||||
(content-length (:content-length data))
|
||||
(last-modified (:last-modified data)))))))
|
||||
|
||||
;; In Clojure 1.5.1, the as-file function does not correctly decode
|
||||
;; UTF-8 byte sequences.
|
||||
;;
|
||||
;; See: http://dev.clojure.org/jira/browse/CLJ-1177
|
||||
;;
|
||||
;; As a work-around, we'll backport the fix from CLJ-1177 into
|
||||
;; url-as-file.
|
||||
|
||||
(defn- ^File url-as-file [^java.net.URL u]
|
||||
(-> (.getFile u)
|
||||
(str/replace \/ File/separatorChar)
|
||||
(str/replace "+" (URLEncoder/encode "+" "UTF-8"))
|
||||
(URLDecoder/decode "UTF-8")
|
||||
io/as-file))
|
||||
|
||||
(defn content-type
|
||||
"Returns an updated Ring response with the a Content-Type header corresponding
|
||||
to the given content-type."
|
||||
[resp content-type]
|
||||
(header resp "Content-Type" content-type))
|
||||
|
||||
(defn find-header
|
||||
"Looks up a header in a Ring response (or request) case insensitively,
|
||||
returning the header map entry, or nil if not present."
|
||||
{:added "1.4"}
|
||||
[resp ^String header-name]
|
||||
(->> (:headers resp)
|
||||
(filter #(.equalsIgnoreCase header-name (key %)))
|
||||
(first)))
|
||||
|
||||
(defn get-header
|
||||
"Looks up a header in a Ring response (or request) case insensitively,
|
||||
returning the value of the header, or nil if not present."
|
||||
{:added "1.2"}
|
||||
[resp header-name]
|
||||
(some-> resp (find-header header-name) val))
|
||||
|
||||
(defn update-header
|
||||
"Looks up a header in a Ring response (or request) case insensitively,
|
||||
then updates the header with the supplied function and arguments in the
|
||||
manner of update-in."
|
||||
{:added "1.4"}
|
||||
[resp header-name f & args]
|
||||
(let [header-key (or (some-> resp (find-header header-name) key) header-name)]
|
||||
(update-in resp [:headers header-key] #(apply f % args))))
|
||||
|
||||
(defn charset
|
||||
"Returns an updated Ring response with the supplied charset added to the
|
||||
Content-Type header."
|
||||
{:added "1.1"}
|
||||
[resp charset]
|
||||
(update-header resp "Content-Type"
|
||||
(fn [content-type]
|
||||
(-> (or content-type "text/plain")
|
||||
(str/replace #";\s*charset=[^;]*" "")
|
||||
(str "; charset=" charset)))))
|
||||
|
||||
(defn get-charset
|
||||
"Gets the character encoding of a Ring response."
|
||||
{:added "1.6"}
|
||||
[resp]
|
||||
(some-> (get-header resp "Content-Type")
|
||||
parsing/find-content-type-charset))
|
||||
|
||||
(defn set-cookie
|
||||
"Sets a cookie on the response. Requires the handler to be wrapped in the
|
||||
wrap-cookies middleware."
|
||||
{:added "1.1"}
|
||||
[resp name value & [opts]]
|
||||
(assoc-in resp [:cookies name] (merge {:value value} opts)))
|
||||
|
||||
(defn response?
|
||||
"True if the supplied value is a valid response map."
|
||||
{:added "1.1"}
|
||||
[resp]
|
||||
(and (map? resp)
|
||||
(integer? (:status resp))
|
||||
(map? (:headers resp))))
|
||||
|
||||
(defmulti resource-data
|
||||
"Returns data about the resource specified by url, or nil if an
|
||||
appropriate resource does not exist.
|
||||
|
||||
The return value is a map with optional values for:
|
||||
:content - the content of the URL, suitable for use as the :body
|
||||
of a ring response
|
||||
:content-length - the length of the :content, nil if not available
|
||||
:last-modified - the Date the :content was last modified, nil if not
|
||||
available
|
||||
|
||||
This dispatches on the protocol of the URL as a keyword, and
|
||||
implementations are provided for :file and :jar. If you are on a
|
||||
platform where (Class/getResource) returns URLs with a different
|
||||
protocol, you will need to provide an implementation for that
|
||||
protocol.
|
||||
|
||||
This function is used internally by url-response."
|
||||
{:arglists '([url]), :added "1.4"}
|
||||
(fn [^java.net.URL url]
|
||||
(keyword (.getProtocol url))))
|
||||
|
||||
(defmethod resource-data :file
|
||||
[url]
|
||||
(if-let [file (url-as-file url)]
|
||||
(if-not (.isDirectory file)
|
||||
(file-data file))))
|
||||
|
||||
(defn- add-ending-slash [^String path]
|
||||
(if (.endsWith path "/")
|
||||
path
|
||||
(str path "/")))
|
||||
|
||||
(defn- jar-directory? [^java.net.JarURLConnection conn]
|
||||
(let [jar-file (.getJarFile conn)
|
||||
entry-name (.getEntryName conn)
|
||||
dir-entry (.getEntry jar-file (add-ending-slash entry-name))]
|
||||
(and dir-entry (.isDirectory dir-entry))))
|
||||
|
||||
(defn- connection-content-length [^java.net.URLConnection conn]
|
||||
(let [len (.getContentLength conn)]
|
||||
(if (<= 0 len) len)))
|
||||
|
||||
(defn- connection-last-modified [^java.net.URLConnection conn]
|
||||
(let [last-mod (.getLastModified conn)]
|
||||
(if-not (zero? last-mod)
|
||||
(Date. last-mod))))
|
||||
|
||||
(defmethod resource-data :jar
|
||||
[^java.net.URL url]
|
||||
(let [conn (.openConnection url)]
|
||||
(if-not (jar-directory? conn)
|
||||
{:content (.getInputStream conn)
|
||||
:content-length (connection-content-length conn)
|
||||
:last-modified (connection-last-modified conn)})))
|
||||
|
||||
(defn url-response
|
||||
"Return a response for the supplied URL."
|
||||
{:added "1.2"}
|
||||
[^URL url]
|
||||
(if-let [data (resource-data url)]
|
||||
(-> (response (:content data))
|
||||
(content-length (:content-length data))
|
||||
(last-modified (:last-modified data)))))
|
||||
|
||||
(defn- get-resources [path ^ClassLoader loader]
|
||||
(-> (or loader (.getContextClassLoader (Thread/currentThread)))
|
||||
(.getResources path)
|
||||
(enumeration-seq)))
|
||||
|
||||
(defn- safe-file-resource? [{:keys [body]} {:keys [root loader allow-symlinks?]}]
|
||||
(or allow-symlinks?
|
||||
(nil? root)
|
||||
(let [root (.replaceAll (str root) "^/" "")]
|
||||
(or (str/blank? root)
|
||||
(let [path (canonical-path body)]
|
||||
(some #(and (= "file" (.getProtocol ^URL %))
|
||||
(.startsWith path (canonical-path (url-as-file %))))
|
||||
(get-resources root loader)))))))
|
||||
|
||||
(defn resource-response
|
||||
"Returns a Ring response to serve a packaged resource, or nil if the
|
||||
resource does not exist.
|
||||
Options:
|
||||
:root - take the resource relative to this root
|
||||
:loader - resolve the resource in this class loader
|
||||
:allow-symlinks? - allow symlinks that lead to paths outside the root
|
||||
classpath directories (defaults to false)"
|
||||
([path]
|
||||
(resource-response path {}))
|
||||
([path options]
|
||||
(let [path (-> (str "/" path) (.replace "//" "/"))
|
||||
root+path (-> (str (:root options) path) (.replaceAll "^/" ""))
|
||||
;; babashka patch
|
||||
io-resource-fn (or (get-in options [:static :io-resource-fn]) io/resource)
|
||||
load #(if-let [loader (:loader options)]
|
||||
(io-resource-fn % loader)
|
||||
(io-resource-fn %))]
|
||||
(if-not (directory-transversal? root+path)
|
||||
(if-let [resource (load root+path)]
|
||||
(let [response (url-response resource)]
|
||||
(if (or (not (instance? File (:body response)))
|
||||
;; babashka patch
|
||||
true #_(safe-file-resource? response options))
|
||||
response)))))))
|
||||
11
feature-selmer/babashka/impl/selmer_parser.clj
Normal file
11
feature-selmer/babashka/impl/selmer_parser.clj
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
(ns babashka.impl.selmer-parser
|
||||
(:require [selmer.parser :as parser]
|
||||
[sci.core :as sci :refer [copy-var]]))
|
||||
|
||||
(def pns (sci/create-ns 'selmer.parser nil))
|
||||
|
||||
(def selmer-parser-namespace
|
||||
{:obj pns
|
||||
'render-file (copy-var parser/render-file pns)
|
||||
'render (copy-var parser/render pns)
|
||||
'set-resource-path! (copy-var parser/set-resource-path! pns)})
|
||||
13
project.clj
13
project.clj
|
|
@ -44,6 +44,16 @@
|
|||
:dependencies [[http-kit "2.5.0"]]}
|
||||
:feature/httpkit-server {:source-paths ["feature-httpkit-server"]
|
||||
:dependencies [[http-kit "2.5.0"]]}
|
||||
:feature/ring {:source-paths ["feature-ring"]
|
||||
:dependencies [[ring/ring-core "1.8.1"]
|
||||
[ring/ring-defaults "0.3.2"]
|
||||
[ring-webjars "0.2.0"]]}
|
||||
:feature/reitit {:source-paths ["feature-reitit"]
|
||||
:dependencies [[metosin/reitit "0.5.5"]
|
||||
[metosin/muuntaja "0.6.7"]
|
||||
[metosin/ring-http-response "0.9.1"]]}
|
||||
:feature/selmer {:source-paths ["feature-selmer"]
|
||||
:dependencies [[selmer "1.12.29"]]}
|
||||
:feature/lanterna {:source-paths ["feature-lanterna"]
|
||||
:dependencies [[babashka/clojure-lanterna "0.9.8-SNAPSHOT"]]}
|
||||
|
||||
|
|
@ -58,6 +68,9 @@
|
|||
:feature/datascript
|
||||
:feature/httpkit-client
|
||||
:feature/httpkit-server
|
||||
:feature/ring
|
||||
:feature/reitit
|
||||
:feature/selmer
|
||||
{:dependencies [[clj-commons/conch "0.9.2"]
|
||||
[com.clojure-goes-fast/clj-async-profiler "0.4.1"]
|
||||
[com.opentable.components/otj-pg-embedded "0.13.3"]]}]
|
||||
|
|
|
|||
|
|
@ -110,6 +110,27 @@ else
|
|||
BABASHKA_LEIN_PROFILES+=",-feature/httpkit-server"
|
||||
fi
|
||||
|
||||
if [ "$BABASHKA_FEATURE_RING" = "true" ]
|
||||
then
|
||||
BABASHKA_LEIN_PROFILES+=",+feature/ring"
|
||||
else
|
||||
BABASHKA_LEIN_PROFILES+=",-feature/ring"
|
||||
fi
|
||||
|
||||
if [ "$BABASHKA_FEATURE_REITIT" = "true" ]
|
||||
then
|
||||
BABASHKA_LEIN_PROFILES+=",+feature/reitit"
|
||||
else
|
||||
BABASHKA_LEIN_PROFILES+=",-feature/reitit"
|
||||
fi
|
||||
|
||||
if [ "$BABASHKA_FEATURE_SELMER" = "true" ]
|
||||
then
|
||||
BABASHKA_LEIN_PROFILES+=",+feature/selmer"
|
||||
else
|
||||
BABASHKA_LEIN_PROFILES+=",-feature/selmer"
|
||||
fi
|
||||
|
||||
if [ "$BABASHKA_FEATURE_LANTERNA" = "true" ]
|
||||
then
|
||||
BABASHKA_LEIN_PROFILES+=",+feature/lanterna"
|
||||
|
|
|
|||
|
|
@ -82,6 +82,24 @@ set BABASHKA_LEIN_PROFILES=%BABASHKA_LEIN_PROFILES%,+feature/httpkit-server
|
|||
set BABASHKA_LEIN_PROFILES=%BABASHKA_LEIN_PROFILES%,-feature/httpkit-server
|
||||
)
|
||||
|
||||
if "%BABASHKA_FEATURE_RING%"=="true" (
|
||||
set BABASHKA_LEIN_PROFILES=%BABASHKA_LEIN_PROFILES%,+feature/ring
|
||||
) else (
|
||||
set BABASHKA_LEIN_PROFILES=%BABASHKA_LEIN_PROFILES%,-feature/ring
|
||||
)
|
||||
|
||||
if "%BABASHKA_FEATURE_REITIT%"=="true" (
|
||||
set BABASHKA_LEIN_PROFILES=%BABASHKA_LEIN_PROFILES%,+feature/reitit
|
||||
) else (
|
||||
set BABASHKA_LEIN_PROFILES=%BABASHKA_LEIN_PROFILES%,-feature/reitit
|
||||
)
|
||||
|
||||
if "%BABASHKA_FEATURE_SELMER%"=="true" (
|
||||
set BABASHKA_LEIN_PROFILES=%BABASHKA_LEIN_PROFILES%,+feature/selmer
|
||||
) else (
|
||||
set BABASHKA_LEIN_PROFILES=%BABASHKA_LEIN_PROFILES%,-feature/selmer
|
||||
)
|
||||
|
||||
if "%BABASHKA_FEATURE_LANTERNA%"=="true" (
|
||||
set BABASHKA_LEIN_PROFILES=%BABASHKA_LEIN_PROFILES%,+feature/lanterna
|
||||
) else (
|
||||
|
|
|
|||
|
|
@ -18,4 +18,7 @@
|
|||
(def oracledb? (= "true" (System/getenv "BABASHKA_FEATURE_ORACLEDB")))
|
||||
(def hsqldb? (= "true" (System/getenv "BABASHKA_FEATURE_HSQLDB")))
|
||||
(def datascript? (= "true" (System/getenv "BABASHKA_FEATURE_DATASCRIPT")))
|
||||
(def ring? (= "true" (System/getenv "BABASHKA_FEATURE_RING")))
|
||||
(def reitit? (= "true" (System/getenv "BABASHKA_FEATURE_REITIT")))
|
||||
(def selmer? (= "true" (System/getenv "BABASHKA_FEATURE_SELMER")))
|
||||
(def lanterna? (= "true" (System/getenv "BABASHKA_FEATURE_LANTERNA")))
|
||||
|
|
|
|||
|
|
@ -90,6 +90,23 @@
|
|||
(when features/httpkit-server?
|
||||
(require '[babashka.impl.httpkit-server]))
|
||||
|
||||
(when features/ring?
|
||||
(require '[babashka.impl.ring-middleware-defaults]
|
||||
'[babashka.impl.ring-util-response]
|
||||
'[babashka.impl.ring-util-request]
|
||||
'[babashka.impl.ring-middleware-content-type]
|
||||
'[babashka.impl.ring-middleware-webjars]
|
||||
'[babashka.impl.ring-middleware-anti-forgery]))
|
||||
|
||||
(when features/reitit?
|
||||
(require '[babashka.impl.reitit-ring]
|
||||
'[babashka.impl.ring-util-http-response]
|
||||
'[babashka.impl.muuntaja-core]
|
||||
'[babashka.impl.muuntaja-middleware]))
|
||||
|
||||
(when features/selmer?
|
||||
(require '[babashka.impl.selmer-parser]))
|
||||
|
||||
(when features/lanterna?
|
||||
(require '[babashka.impl.lanterna]))
|
||||
|
||||
|
|
@ -405,6 +422,21 @@ If neither -e, -f, or --socket-repl are specified, then the first argument that
|
|||
features/httpkit-client? (assoc 'org.httpkit.client @(resolve 'babashka.impl.httpkit-client/httpkit-client-namespace)
|
||||
'org.httpkit.sni-client @(resolve 'babashka.impl.httpkit-client/sni-client-namespace))
|
||||
features/httpkit-server? (assoc 'org.httpkit.server @(resolve 'babashka.impl.httpkit-server/httpkit-server-namespace))
|
||||
|
||||
features/ring? (-> (assoc 'ring.middleware.defaults @(resolve 'babashka.impl.ring-middleware-defaults/ring-middleware-defaults-namespace))
|
||||
(assoc 'ring.util.response @(resolve 'babashka.impl.ring-util-response/ring-util-response-namespace))
|
||||
(assoc 'ring.util.request @(resolve 'babashka.impl.ring-util-request/ring-util-request-namespace))
|
||||
(assoc 'ring.middleware.content-type @(resolve 'babashka.impl.ring-middleware-content-type/ring-middleware-content-type-namespace))
|
||||
(assoc 'ring.middleware.webjars @(resolve 'babashka.impl.ring-middleware-webjars/ring-middleware-webjars-namespace))
|
||||
(assoc 'ring.middleware.anti-forgery @(resolve 'babashka.impl.ring-middleware-anti-forgery/ring-middleware-anti-forgery-namespace)))
|
||||
|
||||
features/reitit? (-> (assoc 'reitit.ring @(resolve 'babashka.impl.reitit-ring/reitit-ring-namespace))
|
||||
(assoc 'ring.util.http-response @(resolve 'babashka.impl.ring-util-http-response/ring-util-http-response-namespace))
|
||||
(assoc 'muuntaja.core @(resolve 'babashka.impl.muuntaja-core/muuntaja-core-namespace))
|
||||
(assoc 'muuntaja.middleware @(resolve 'babashka.impl.muuntaja-middleware/muuntaja-middleware-namespace)))
|
||||
|
||||
features/selmer? (assoc 'selmer.parser @(resolve 'babashka.impl.selmer-parser/selmer-parser-namespace))
|
||||
|
||||
features/lanterna? (assoc 'lanterna.screen @(resolve 'babashka.impl.lanterna/lanterna-screen-namespace)
|
||||
'lanterna.terminal @(resolve 'babashka.impl.lanterna/lanterna-terminal-namespace)
|
||||
'lanterna.constants @(resolve 'babashka.impl.lanterna/lanterna-constants-namespace))))
|
||||
|
|
|
|||
Loading…
Reference in a new issue