(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))]]) | |