(ns metabase.lib.schema.util (:refer-clojure :exclude [ref]) (:require [clojure.walk :as walk] [metabase.lib.options :as lib.options] [metabase.util :as u] [metabase.util.malli.registry :as mr])) | |
(declare collect-uuids*) | |
(defn- collect-uuids-in-map [m result] (when-let [our-uuid (or (:lib/uuid (lib.options/options m)) (:lib/uuid m))] ;; Keep duplicates in metadata of the result. (if (@result our-uuid) (vswap! result vary-meta update :duplicates (fnil conj #{}) our-uuid) (vswap! result conj our-uuid))) (reduce-kv (fn [_ k v] (when (not (qualified-keyword? k)) (collect-uuids* v result))) nil m)) | |
(defn- collect-uuids-in-sequence [xs result] (run! #(collect-uuids* % result) xs)) | |
(defn- collect-uuids* [x result] (cond (map? x) (collect-uuids-in-map x result) (sequential? x) (collect-uuids-in-sequence x result) :else nil)) | |
Return all the | (defn collect-uuids [x] (let [result (volatile! #{})] (collect-uuids* x result) @result)) |
(defn- find-duplicate-uuid [x] (:duplicates (meta (collect-uuids x)))) | |
True if all the | (defn unique-uuids? [x] (empty? (find-duplicate-uuid x))) |
Malli schema for to ensure that all | (mr/def ::unique-uuids [:fn {:error/message "all :lib/uuids must be unique" :error/fn (fn [{:keys [value]} _] (str "Duplicate :lib/uuid " (pr-str (find-duplicate-uuid value))))} #'unique-uuids?]) |
Is a sequence of | (defn distinct-refs? [refs] (or (< (count refs) 2) (apply distinct? (for [ref refs] (let [options (lib.options/options ref)] (lib.options/with-options ref ;; Using reduce-kv to remove namespaced keys and some other keys to perform the comparison. (reduce-kv (fn [acc k _] (if (or (qualified-keyword? k) (#{:base-type :effective-type :ident} k)) (dissoc acc k) acc)) options options))))))) |
Recursively remove all uuids, | (defn remove-randomized-idents [x] (walk/postwalk (fn [x] (if (map? x) (dissoc x :lib/uuid :ident :entity_id :entity-id) x)) x)) |
Convert all order-bys in a stage to refer to aggregations by index instead of uuid | (defn- indexed-order-bys-for-stage [{:keys [aggregation order-by] :as stage}] (if (and aggregation order-by) (let [agg-lookups (->> aggregation (map-indexed (fn [i [_type {agg-uuid :lib/uuid}]] [agg-uuid i])) (into {}))] (update stage :order-by (fn [order-bys] (mapv (fn [[_dir _opts [order-type agg-opts agg-uuid] :as order-by]] (if (= order-type :aggregation) (assoc order-by 2 [order-type agg-opts (agg-lookups agg-uuid)]) order-by)) order-bys)))) stage)) |
Convert all order-bys in a query to refer to aggregations by index instead of uuid. The result is not a valid query, but avoiding random uuids is important during hashing. | (defn indexed-order-bys [query] (if (:stages query) (update query :stages #(mapv indexed-order-bys-for-stage %)) query)) |
(mr/def ::distinct-ignoring-uuids [:fn {:error/message "values must be distinct ignoring uuids" :error/fn (fn [{:keys [value]} _] (str "Duplicate values ignoring uuids in: " (pr-str (remove-randomized-idents value))))} (comp u/empty-or-distinct? remove-randomized-idents)]) | |
Add an additional constraint to | (defn distinct-ignoring-uuids [schema] [:and schema [:ref ::distinct-ignoring-uuids]]) |