(ns metabase.lib.schema.template-tag (:require [malli.core :as mc] [metabase.lib.schema.common :as common] [metabase.lib.schema.id :as id] [metabase.lib.schema.parameter :as lib.schema.parameter] [metabase.util.malli.registry :as mr])) | |
Schema for valid values of | (mr/def ::widget-type (into [:enum ;; this will be a nicer error message than Malli trying to list every single possible allowed type. {:decode/normalize common/normalize-keyword :error/message "Valid template tag :widget-type"} :none] ;; TODO -- move this stuff into `metabase.lib` (keys lib.schema.parameter/types))) |
Schema for valid values of template tag | (mr/def ::type [:enum {:decode/normalize common/normalize-keyword} :snippet :card :dimension :number :text :date]) |
(mr/def ::name [:ref {:decode/normalize common/normalize-string-key} ::common/non-blank-string]) | |
Things required by all template tag types. | (mr/def ::common [:map [:name ::name] [:display-name ::common/non-blank-string] ;; TODO -- `:id` is actually 100% required but we have a lot of tests that don't specify it because this constraint ;; wasn't previously enforced; we need to go in and fix those tests and make this non-optional [:id {:optional true} [:multi {:dispatch uuid?} [true :uuid] [false ::common/non-blank-string]]]]) |
Stuff shared between the Field filter and raw value template tag schemas. | (mr/def ::value.common [:merge [:ref ::common] [:map ;; default value for this parameter [:default {:optional true} any?] ;; whether or not a value for this parameter is required in order to run the query [:required {:optional true} :boolean]]]) |
Example: {:id "c20851c7-8a80-0ffa-8a99-ae636f0e9539" :name "date" :display-name "Date" :type :dimension, :dimension [:field 4 nil] :widget-type :date/all-options} | (mr/def ::field-filter [:merge [:ref ::value.common] [:map [:type [:= :dimension]] [:dimension [:ref :mbql.clause/field]] ;; which type of widget the frontend should show for this Field Filter; this also affects which parameter types ;; are allowed to be specified for it. [:widget-type [:ref ::widget-type]] ;; optional map to be appended to filter clause [:options {:optional true} [:maybe :map]]]]) |
(mr/def ::disallow-dimension [:fn {:decode/normalize #(dissoc % :dimension) :error/message ":dimension is only allowed for :type :dimension template tags"} #(not (contains? % :dimension))]) | |
Example: {:id "c2fc7310-44eb-4f21-c3a0-63806ffb7ddd" :name "snippet: select" :display-name "Snippet: select" :type :snippet :snippet-name "select" :snippet-id 1} | (mr/def ::snippet [:and [:merge [:ref ::common] [:map [:type [:= :snippet]] [:snippet-name ::common/non-blank-string] [:snippet-id {:optional true} ::id/snippet] ;; database to which this Snippet belongs. Doesn't always seem to be specified. [:database {:optional true} ::id/database]]] [:ref ::disallow-dimension]]) |
Example: {:id "fc5e14d9-7d14-67af-66b2-b2a6e25afeaf" :name "#1635" :display-name "#1635" :type :card :card-id 1635} | (mr/def ::source-query [:and [:merge [:ref ::common] [:map [:type [:= :card]] [:card-id ::id/card]]] [:ref ::disallow-dimension]]) |
Set of valid values of | (def raw-value-template-tag-types #{:number :text :date :boolean}) |
Valid values of | (mr/def ::raw-value.type (into [:enum] raw-value-template-tag-types)) |
Example: {:id "35f1ecd4-d622-6d14-54be-750c498043cb" :name "id" :display-name "Id" :type :number :required true :default "1"} | (mr/def ::raw-value [:and [:merge [:ref ::value.common] ;; `:type` is used be the FE to determine which type of widget to display for the template tag, and to determine ;; which types of parameters are allowed to be passed in for this template tag. [:map [:type [:ref ::raw-value.type]]]] [:ref ::disallow-dimension]]) |
(mr/def ::template-tag [:and {:decode/normalize common/normalize-map} [:map [:type [:ref ::type]]] [:multi {:dispatch #(keyword (:type %))} [:dimension [:ref ::field-filter]] [:snippet [:ref ::snippet]] [:card [:ref ::source-query]] ;; :number, :text, :date [::mc/default [:ref ::raw-value]]]]) | |
(mr/def ::template-tag-map [:and [:map-of ::name ::template-tag] ;; make sure people don't try to pass in a `:name` that's different from the actual key in the map. [:fn {:error/message "keys in template tag map must match the :name of their values"} (fn [m] (every? (fn [[tag-name tag-definition]] (= tag-name (:name tag-definition))) m))]]) | |