Skip to content

Commit

Permalink
misc api refinements
Browse files Browse the repository at this point in the history
- refine api-3 implementation (on-success, on-failure, on-error)
- add api/extract-bearer-token
- make authorization ns configurable (fn used to get model type)
- refine assert-http-status
  • Loading branch information
dgknght committed Jan 14, 2024
1 parent eba62dd commit 358f3d5
Show file tree
Hide file tree
Showing 8 changed files with 233 additions and 54 deletions.
4 changes: 2 additions & 2 deletions .clj-kondo/config.edn
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,12 @@
dgknght.app-lib.test-assertions/same-date?
dgknght.app-lib.web-mocks/called?
dgknght.app-lib.web-mocks/called-with-headers?]}
:unresolved-namespace {:exclude [js]}
:namespace-name-mismatch {:level :off}
:unused-referred-var {:exclude {clojure.pprint [pprint]
cljs.pprint [pprint]}}
:unused-namespace {:exclude [clojure.pprint
cljs.pprint]}
:unresolved-namespace {:exclude [js]}
:namespace-name-mismatch {:level :off}
:clojure-lsp/unused-public-var {:level :off}
:deprecated-var
{:exclude
Expand Down
2 changes: 1 addition & 1 deletion project.clj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(defproject com.github.dgknght/app-lib "0.3.9"
(defproject com.github.dgknght/app-lib "0.3.10"
:description "Library of commonly used functions for web app development"
:url "https://github.com/dgknght/app-lib"
:license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
Expand Down
8 changes: 7 additions & 1 deletion src/clj/dgknght/app_lib/api.clj
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,19 @@
(def internal-server-error
(response {:message "internal server error"} 500))

(defn extract-token-bearer
"Given a request, extracts the token from the authorization header."
[{:keys [headers]}]
(when-let [authorization (headers "authorization")]
(re-find #"(?<=^Bearer ).*" authorization)))

(defn wrap-authentication
"Wraps the handler with an authentication lookup, passing
the request on to the handle step in the handler if the authentication
function returns non-nil, otherwise returing an unauthenticated response.
Options:
:authentication-fn - A function accepting one argument (the request) and returning the authentication result, or nil"
:authenticate-fn - A function accepting one argument (the request) and returning the authentication result, or nil"
[handler {:keys [authenticate-fn]}]
(fn [req]
(if-let [authenticated (authenticate-fn req)]
Expand Down
19 changes: 16 additions & 3 deletions src/clj/dgknght/app_lib/authorization.clj
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,24 @@
(derive ::update ::manage)
(derive ::destroy ::manage)

(def ^:dynamic *config* {:type-fn storage/tag})

(defmacro with-config
[config & body]
`(binding [*config* (merge *config* ~config)]
~@body))

(defn wrap-authorization-config
[handler config]
(fn [req]
(with-config config
(handler req))))

(defn- type-of
[model-or-keyword]
(if (keyword? model-or-keyword)
model-or-keyword
(storage/tag model-or-keyword)))
((:type-fn *config*) model-or-keyword)))

(defmulti allowed?
"Returns a truthy or falsey value indicating whether or not the
Expand Down Expand Up @@ -39,7 +52,7 @@
["forbidden" ::forbidden])]
(ex-info msg {:type err-type
:action action
:model (storage/tag model)
:model ((:type-fn *config*) model)
::opaque? opaque?})))

(def denied? (complement allowed?))
Expand Down Expand Up @@ -72,7 +85,7 @@

(defn +scope
([criteria user]
(+scope criteria (storage/tag criteria) user))
(+scope criteria ((:type-fn *config*) criteria) user))
([criteria model-type user]
(if-let [s (scope model-type user)]
(if (empty? criteria)
Expand Down
3 changes: 2 additions & 1 deletion src/cljc/dgknght/app_lib/core.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
(:require [clojure.string :as string]
[clojure.walk]
#?(:clj [clojure.core :as cc])
#?(:clj [clojure.pprint :refer [pprint]])
#?(:clj [clojure.pprint :refer [pprint]]
:cljs [cljs.pprint :refer [pprint]])
#?(:cljs [goog.string :as gstr])
#?(:cljs [dgknght.app-lib.decimal :as d]))
#?(:clj (:import java.util.UUID clojure.lang.IDeref)))
Expand Down
24 changes: 17 additions & 7 deletions src/cljc/dgknght/app_lib/test_assertions/impl.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@
#?(:clj [clojure.data.zip :as zip])
#?(:clj [clojure.data.zip.xml :refer [xml1->]])
#?(:clj [clojure.zip :refer [xml-zip]])
#?(:clj [clojure.pprint :refer [pprint]])
#?(:clj [clojure.pprint :refer [pprint]]
:cljs [cljs.pprint :refer [pprint]])
#?(:clj [clj-time.format :as tf]
:cljs [cljs-time.format :as tf])
#?(:clj [clj-time.coerce :refer [to-date-time]]
:cljs [cljs-time.coerce :refer [to-date-time]])
[lambdaisland.uri :refer [uri
query-string->map]]
#?(:cljs [goog.string])
#?(:cljs [goog.string.format])
#?(:clj [dgknght.app-lib.test :refer [parse-html-body]])
[dgknght.app-lib.models :as models]
[dgknght.app-lib.core :refer [update-in-if
Expand All @@ -22,7 +24,7 @@
#?(:clj (:import java.io.StringWriter)))

#?(:cljs (def fmt goog.string/format)
:clj (def fmt format))
:clj (def fmt clojure.core/format))

(defn report-msg
[& msgs]
Expand Down Expand Up @@ -56,8 +58,10 @@
[msg form]
(let [expected (safe-nth form 1)
actual (safe-nth form 2)]
`(let [actual# (map-indexed #(prune-to %2 (get-in ~expected [%1]))
~actual)
`(let [actual# (->> ~actual
(interleave ~expected)
(partition 2)
(mapv #(apply prune-to %)))
result# (if (= ~expected actual#)
:pass
:fail)
Expand Down Expand Up @@ -219,7 +223,13 @@
(defn assert-http-status
[expected-status msg form]
(let [response (safe-nth form 1)]
`(let [status# (get-in ~response [:status])]
`(let [status# (get-in ~response [:status])
msg# (some #(get-in ~response %)
[[:json-body :error]
[:json-body :message]
[:body :error]
[:body :message]
[:body]])]
{:type (if (= ~expected-status status#)
:pass
:fail)
Expand All @@ -228,7 +238,7 @@
~expected-status
status#))
:expected ~expected-status
:actual status#})))
:actual (fmt "%s: %s" status# msg#)})))

(defn http-success?
[msg form]
Expand All @@ -240,7 +250,7 @@
(:status ~response)
~msg)
:expected "20x"
:actual (:status ~response)}))
:actual (fmt "%s: %s" (:status ~response) (:body ~response))}))

(defn comparable-uri
[input]
Expand Down
58 changes: 49 additions & 9 deletions src/cljs/dgknght/app_lib/api_3.cljs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
(ns dgknght.app-lib.api-3
(:refer-clojure :exclude [get])
(:require [cljs.core.async :as a]
[cljs.pprint :refer [pprint]]
[cljs-http.client :as http]
[dgknght.app-lib.api :as og]))

(defrecord Error [message])

(def default-opts
{:headers {"Content-Type" "application/json"
"Accept" "application/json"}})
Expand All @@ -23,26 +26,63 @@
x)
[]))

(def ^:private status-msgs
{404 "Not found"
403 "Forbidden"
401 "Unauthorized"
500 "Server error"})

(defn- extract-msg
[{:keys [body status]}]
(or (:message body)
(:error body)
(status-msgs status)
"Unknown"))

(defn- handle-non-success-status
[{:keys [status] :as res}]
(if (<= 200 status 299)
res
(throw (ex-info "non success response" {::message (extract-msg res)}))))

(defn- non-success-msg
[e]
(-> e ex-data ::message))

(defn- build-xf
[{:keys [pre-xf post-xf]}]
(apply comp
(concat (pluralize pre-xf)
[(map #(or (:body %) %))]
[(map handle-non-success-status)
(map #(or (:body %) %))]
(pluralize post-xf))))

(defn- ex-handler
[{:keys [on-error]}]
(fn [e]
(if-let [msg (non-success-msg e)]
(->Error msg)
(if on-error
(on-error e)
(->Error (.getMessage e))))))

(defn- build-chan
[{:as opts
:keys [on-error]
:or {on-error (fn [e]
(println "An error occurred processing the channel request: " e))}}]
(a/chan 1 (build-xf opts) on-error))
[opts]
(a/chan 1 (build-xf opts) (ex-handler opts)))

(defn- wait-and-callback
[ch {:keys [callback]
:or {callback identity}}]
[ch {:keys [on-success
on-failure
callback]
:or {on-success identity
on-failure identity
callback identity}}]
(a/go
(let [res (a/<! ch)]
(callback res))))
(callback)
(if (instance? Error res)
(on-failure (:message res))
(on-success res)))))

(defn- build-req
[opts]
Expand Down

0 comments on commit 358f3d5

Please sign in to comment.