diff --git a/project.clj b/project.clj index 1089feb..57bd543 100644 --- a/project.clj +++ b/project.clj @@ -5,4 +5,5 @@ :url "https://opensource.org/licenses/MIT"} :dependencies [[org.clojure/clojure "1.8.0"] [org.clojure/math.numeric-tower "0.0.4"] + [org.clojure/tools.logging "0.3.1"] [selmer "1.10.6"]]) diff --git a/src/adl_support/core.clj b/src/adl_support/core.clj index e4a18cc..2a3630b 100644 --- a/src/adl_support/core.clj +++ b/src/adl_support/core.clj @@ -42,30 +42,46 @@ (split query-string #"\&"))))) +(defn massage-value + [k m] + (let [v (m k) + vr (if + (string? v) + (try + (read-string v) + (catch Exception _ nil)))] + (cond + (nil? v) {} + (= v "") {} + (number? vr) {k vr} + true + {k v}))) + + (defn massage-params "Sending empty strings, or numbers as strings, to the database often isn't - helpful. Massage these `params` to eliminate these problems." - [params] + helpful. Massage these `params` and `form-params` to eliminate these problems. + We must take key field values out of just params, but we should take all other + values out of form-params - because we need the key to load the form in + the first place, but just accepting values of other params would allow spoofing." + [params form-params key-fields] (reduce + merge + ;; do the keyfields first, from params + (reduce merge {} (map - (fn [k] - (let [v (params k) - vr (if - (string? v) - (try - (read-string v) - (catch Exception _ nil)))] - (cond - (nil? v) {} - (= v "") {} - (number? vr) {k vr} - true - {k v}))) + #(massage-value % params) + (filter + #(key-fields (str (name %))) (keys params)))) + ;; then merge in everything from form-params, potentially overriding what + ;; we got from params. + (map + #(massage-value % form-params) + (keys form-params)))) -(massage-params {:a "a" :b "1" :c nil}) (defn raw-resolve-template @@ -75,5 +91,25 @@ n (str "auto/" n))) + (def resolve-template (memoize raw-resolve-template)) + +(defmacro do-or-log-error + "Evaluate the supplied `form` in a try/catch block. If the + keyword param `:message` is supplied, the value will be used + as the log message; if the keyword param `:error-return` is + supplied, the value will be returned if an exception is caught." + [form & {:keys [message error-return] + :or {message `(str "A failure occurred in " + ~(list 'quote form))}}] + `(try + ~form + (catch Exception any# + (clojure.tools.logging/error + (str ~message + (with-out-str + (-> any# .printStackTrace)))) + ~error-return))) + + diff --git a/src/adl_support/utils.clj b/src/adl_support/utils.clj index bf00b19..6032f15 100644 --- a/src/adl_support/utils.clj +++ b/src/adl_support/utils.clj @@ -4,6 +4,7 @@ (:require [clojure.math.numeric-tower :refer [expt]] [clojure.pprint :as p] [clojure.string :as s] + [clojure.tools.logging :as log] [clojure.xml :as x])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -22,6 +23,7 @@ ;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; TODO: this really ought to be split into several namespaces (def ^:dynamic *locale* "The locale for which files will be generated." @@ -343,6 +345,15 @@ (filter #(#{"user" "all"} (:distinct (:attrs %))) (all-properties entity))) +(defn user-distinct-property-names + "Return, as a set, the names of properties which are user distinct" + [entity] + (set + (map + (fn [x] (-> x :attrs :name)) + (user-distinct-properties entity)))) + + (defmacro insertable-properties "Return all the properties of this `entity` (including key properties) into which user-supplied data can be inserted"