(ns metabase.lib.normalize (:require [malli.core :as mc] [malli.transform :as mtx] [metabase.lib.schema :as lib.schema] [metabase.lib.schema.mbql-clause :as lib.schema.mbql-clause] [metabase.lib.schema.metadata :as lib.schema.metadata] [metabase.util :as u] [metabase.util.i18n :as i18n] [metabase.util.log :as log] [metabase.util.malli.registry :as mr])) | |
(defn- lib-type [x] (when (map? x) (keyword (some #(get x %) [:lib/type "lib/type"])))) | |
TODO -- we are missing some stuff for sure. | (def ^:private lib-type->schema {:mbql/query ::lib.schema/query :mbql.stage/mbql ::lib.schema/stage.mbql :mbql.stage/native ::lib.schema/stage.native :metadata/database ::lib.schema.metadata/database :metadata/table ::lib.schema.metadata/table :metadata/column ::lib.schema.metadata/column :metadata/card ::lib.schema.metadata/card :metadata/segment ::lib.schema.metadata/segment :metadata/metric ::lib.schema.metadata/metric}) |
(defn- infer-schema [x] (cond (map? x) (or (-> x lib-type lib-type->schema) :map) (and (vector? x) ((some-fn simple-keyword? string?) (first x))) (lib.schema.mbql-clause/tag->registered-schema-name (first x)) :else :any)) | |
If normalization errors somewhere, just log the error and return the partially-normalized result. Easier to debug this way. | (defn- default-error-fn [error] (log/warnf "Error normalizing pMBQL:\n%s" (u/pprint-to-str error)) (:value error)) |
(def ^:private ^:dynamic *error-fn* default-error-fn) | |
(defn- coercer [schema] (mr/cached ::coercer schema (fn [] (let [respond identity raise #'*error-fn*] ; capture var rather than the bound value at the time this is eval'ed (mc/coercer schema (mtx/transformer {:name :normalize}) respond raise))))) | |
Ensure some part of an MBQL query Normalization logic is defined in various schemas; grep for The default implementation will keywordize keys for maps, and convert some known keys using [[default-map-value-fns]]; for MBQL clauses, it will convert the clause name to a keyword and recursively normalize its options and arguments. Implement this method if you need custom behavior for something. Pass in a By default, does not throw Exceptions -- just logs them and returns what it was able to normalize, but you can pass
in the option | (defn normalize ([x] (normalize nil x)) ([schema x] (normalize schema x nil)) ([schema x {:keys [throw?], :or {throw? false}, :as _options}] (let [schema (or schema (infer-schema x)) thunk (^:once fn* [] ((coercer schema) x))] (if throw? (binding [*error-fn* (fn [error] (throw (ex-info (i18n/tru "Normalization error") {:schema schema, :x x, :error error})))] (thunk)) (thunk))))) |