Skip to content
Snippets Groups Projects
Commit 65a8c23d authored by Cam Saul's avatar Cam Saul
Browse files

Fix accidental detached head wonky git shennanigans

parents e18fa58e 1bd68a4e
No related branches found
No related tags found
No related merge requests found
......@@ -180,11 +180,13 @@
Dispatches on the arg annotation as a keyword, and is also passed the symbol
of the argument that should be checked.
(defendpoint GET ... [id.required])
(defendpoint GET ... [id] {id required})
-> (let [id ~(arg-annotation-fn :required id)] ...)
-> (let [id ~(arg-annotation-fn :required id)]
...)
-> (let [id (do (require-params id) id)] ...)"
-> (let [id (do (require-params id) id)]
...)"
(fn [annotation-kw arg-symb]
{:pre [(keyword? annotation-kw)
(symbol? arg-symb)]}
......@@ -194,32 +196,28 @@
(defmethod arg-annotation-fn :default [annotation-kw arg-symbol]
(throw (Exception. (format "Don't know what to do with arg annotation '%s' on arg '%s'!" (name annotation-kw) (name arg-symbol)))))
;; ### defannotation
(defmacro defannotation
"Convenience for defining a new `defendpoint` arg annotation.
BINDING is the actual symbol name of the arg being checked; `defannotation` returns form(s)
that will be included in the let binding for the annotated arg.
(defannotation required [param]
(defannotation Required [param]
`(require-params ~param) ; quasiquoting needed to keep require-params from being evaluated at macroexpansion time
param)"
[annotation-name [binding] & body]
`(defmethod arg-annotation-fn ~(keyword annotation-name) [~'_ ~binding]
`(do ~~@body)))
;; `required` just calls require-params
(defannotation required [param]
`(require-params ~param)
param)
;; ### common annotation definitions
;; `req` is an alias for `required`
(defannotation req [param]
;; `required` just calls require-params
(defannotation Required [param]
`(require-params ~param)
param)
(defannotation int [param]
`(Integer/parseInt ~param))
;;; ### defendpoint
......@@ -229,10 +227,12 @@
- calls `auto-parse` to automatically parse certain args. e.g. `id` is converted from `String` to `Integer` via `Integer/parseInt`
- converts ROUTE from a simple form like `\"/:id\"` to a typed one like `[\"/:id\" :id #\"[0-9]+\"]`
- sequentially applies specified annotation functions on args to validate or cast them.
- executes BODY inside a `try-catch` block that handles `ApiExceptions`
- automatically calls `wrap-response-if-needed` on the result of BODY
- tags function's metadata in a way that subsequent calls to `define-routes` (see below)
will automatically include the function in the generated `defroutes` form."
{:arglists '([method route args annotations-map? & body])}
[method route args & more]
{:pre [(or (string? route)
(vector? route))
......
......@@ -9,6 +9,14 @@
[metabase.util :refer [is-email? select-non-nil-keys]]
[metabase.util.password :as password]))
(defannotation Email [email]
`(check (is-email? ~email) [400 (format ~(str (name email) " '%s' is not a valid email.") ~email)])
email)
(defannotation ComplexPassword [password]
`(check (password/is-complex? ~password) [400 "Insufficient password strength"])
password)
(defendpoint GET "/" []
;; user must be a superuser to proceed
......@@ -26,13 +34,9 @@
(check-403 (or (= id *current-user-id*) (:is_superuser @*current-user*)))
(check-404 (sel :one User :id id)))
(defannotation email [email]
`(require-params ~email)
`(check (is-email? ~email) [400 (format ~(str (name email) " '%s' is not a valid email.") ~email)])
email)
(defendpoint PUT "/:id" [id :as {{:keys [email] :as body} :body}]
{email email}
{email [Required Email]}
;; user must be getting their own details OR they must be a superuser to proceed
(check-403 (or (= id *current-user-id*) (:is_superuser @*current-user*)))
;; can't change email if it's already taken BY ANOTHER ACCOUNT
......@@ -42,13 +46,10 @@
(mapply upd User id)))
(sel :one User :id id))
(defannotation complex-pw [password]
`(check (password/is-complex? ~password) [400 "Insufficient password strength"])
password)
(defendpoint PUT "/:id/password" [id :as {{:keys [password old_password]} :body}]
{password [req complex-pw]
old_password req}
{password [Required ComplexPassword]
old_password Required}
(check-403 (or (= id *current-user-id*)
(:is_superuser @*current-user*)))
(let-404 [user (sel :one [User :password_salt :password] :id id)]
......@@ -56,4 +57,5 @@
(set-user-password id password)
(sel :one User :id id))
(define-routes)
......@@ -176,10 +176,10 @@
(metabase.api.common.internal/auto-parse [id]
(metabase.api.common.internal/catch-api-exceptions
(metabase.api.common.internal/let-annotated-args
{id required}
{id Required}
(clojure.core/-> (do (->404 (sel :one Card :id id)))
metabase.api.common.internal/wrap-response-if-needed))))))
(clojure.core/alter-meta! #'GET_:id clojure.core/assoc :is-endpoint? true))
(defendpoint GET "/:id" [id]
{id required}
{id Required}
(->404 (sel :one Card :id id)))))
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment