From 5ce3ba19b1ec3de4eff8ba58eaac1efd0d9c3a05 Mon Sep 17 00:00:00 2001
From: john-metabase <92878045+john-metabase@users.noreply.github.com>
Date: Tue, 23 May 2023 07:29:45 -0400
Subject: [PATCH] Adds support for options in template tags (#30901)

---
 shared/src/metabase/mbql/schema.cljc          |  4 ++-
 .../driver/common/parameters/values.clj       | 12 +++++---
 .../driver/sql/parameters/substitute_test.clj | 28 +++++++++++++++++--
 3 files changed, 37 insertions(+), 7 deletions(-)

diff --git a/shared/src/metabase/mbql/schema.cljc b/shared/src/metabase/mbql/schema.cljc
index 5fe28f8a9b4..c70614f6455 100644
--- a/shared/src/metabase/mbql/schema.cljc
+++ b/shared/src/metabase/mbql/schema.cljc
@@ -1145,7 +1145,9 @@
     :dimension   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 (s/recursive #'WidgetType)}))
+    :widget-type (s/recursive #'WidgetType)
+    ;; optional map to be appended to filter clause
+    (s/optional-key :options) {s/Keyword s/Any}}))
 
 (def raw-value-template-tag-types
   "Set of valid values of `:type` for raw value template tags."
diff --git a/src/metabase/driver/common/parameters/values.clj b/src/metabase/driver/common/parameters/values.clj
index 70ffaedbd52..103cb14cb1c 100644
--- a/src/metabase/driver/common/parameters/values.clj
+++ b/src/metabase/driver/common/parameters/values.clj
@@ -102,9 +102,12 @@
   multiple values."
   [tag :- mbql.s/TemplateTag params :- (s/maybe [mbql.s/Parameter])]
   (let [matching-params  (tag-params tag params)
+        tag-opts         (:options tag)
         normalize-params (fn [params]
-                           ;; remove `:target` which is no longer needed after this point.
-                           (let [params (map #(dissoc % :target) params)]
+                           ;; remove `:target` which is no longer needed after this point, and add any tag options
+                           (let [params (map #(cond-> (dissoc % :target)
+                                                (seq tag-opts) (assoc :options tag-opts))
+                                             params)]
                              (if (= (count params) 1)
                                (first params)
                                params)))]
@@ -115,8 +118,9 @@
        (normalize-params matching-params))
      ;; otherwise, attempt to fall back to the default value specified as part of the template tag.
      (when-let [tag-default (:default tag)]
-       {:type  (:widget-type tag :dimension) ; widget-type is the actual type of the default value if set
-        :value tag-default})
+       (cond-> {:type    (:widget-type tag :dimension) ; widget-type is the actual type of the default value if set
+                :value   tag-default}
+         tag-opts (assoc :options tag-opts)))
      ;; if that doesn't exist, see if the matching parameters specified default values This can be the case if the
      ;; parameters came from a Dashboard -- Dashboard parameter mappings can specify their own defaults -- but we want
      ;; the defaults specified in the template tag to take precedence if both are specified
diff --git a/test/metabase/driver/sql/parameters/substitute_test.clj b/test/metabase/driver/sql/parameters/substitute_test.clj
index 23e9483c941..b7ab05c6244 100644
--- a/test/metabase/driver/sql/parameters/substitute_test.clj
+++ b/test/metabase/driver/sql/parameters/substitute_test.clj
@@ -113,7 +113,7 @@
   (testing "new operators"
     (testing "string operators"
       (let [query ["select * from venues where " (param "param")]]
-        (doseq [[operator {:keys [field value expected]}]
+        (doseq [[operator {:keys [field value expected options]}]
                 (partition-all
                  2
                  [:string/contains         {:field    :name
@@ -125,6 +125,16 @@
                                                         "where"
                                                         "  (\"PUBLIC\".\"VENUES\".\"NAME\" LIKE ?)"]
                                                        ["%foo%"]]}
+                  :string/contains         {:field    :name
+                                            :value    ["FOO"]
+                                            :options  {:case-sensitive false}
+                                            :expected [["select"
+                                                        "  *"
+                                                        "from"
+                                                        "  venues"
+                                                        "where"
+                                                        "  (LOWER(\"PUBLIC\".\"VENUES\".\"NAME\") LIKE ?)"]
+                                                       ["%foo%"]]}
                   :string/does-not-contain {:field    :name
                                             :value    ["foo"]
                                             :expected [["select"
@@ -137,6 +147,19 @@
                                                         "    OR (\"PUBLIC\".\"VENUES\".\"NAME\" IS NULL)"
                                                         "  )"]
                                                        ["%foo%"]]}
+                  :string/does-not-contain {:field    :name
+                                            :value    ["FOO"]
+                                            :options  {:case-sensitive false}
+                                            :expected [["select"
+                                                        "  *"
+                                                        "from"
+                                                        "  venues"
+                                                        "where"
+                                                        "  ("
+                                                        "    NOT (LOWER(\"PUBLIC\".\"VENUES\".\"NAME\") LIKE ?)"
+                                                        "    OR (\"PUBLIC\".\"VENUES\".\"NAME\" IS NULL)"
+                                                        "  )"]
+                                                       ["%foo%"]]}
                   :string/starts-with      {:field    :name
                                             :value    ["foo"]
                                             :expected [["select"
@@ -265,7 +288,8 @@
                    (-> (substitute query {"param" (params/map->FieldFilter
                                                    {:field (t2/select-one Field :id (mt/id :venues field))
                                                     :value {:type  operator
-                                                            :value value}})})
+                                                            :value value
+                                                            :options options}})})
                        vec
                        (update 0 mdb.query/format-sql :h2)
                        (update 0 str/split-lines))))))))))
-- 
GitLab