From 18712b24e46e8b4e1dee496a1575888735e035e5 Mon Sep 17 00:00:00 2001
From: Ryan Senior <ryan@metabase.com>
Date: Mon, 12 Jun 2017 18:02:42 -0500
Subject: [PATCH] Added dimension_options to the query_metadata response

---
 src/metabase/api/table.clj       | 66 ++++++++++++++++++++++++++++++++
 test/metabase/api/table_test.clj | 42 +++++++++++++-------
 2 files changed, 95 insertions(+), 13 deletions(-)

diff --git a/src/metabase/api/table.clj b/src/metabase/api/table.clj
index 91909a55583..ed8ad5c484e 100644
--- a/src/metabase/api/table.clj
+++ b/src/metabase/api/table.clj
@@ -2,6 +2,7 @@
   "/api/table endpoints."
   (:require [clojure.tools.logging :as log]
             [compojure.core :refer [GET PUT]]
+            [medley.core :as m]
             [metabase
              [sync-database :as sync-database]
              [util :as u]]
@@ -76,6 +77,70 @@
         (sync-database/sync-table! updated-table))
       updated-table)))
 
+(def ^:private dimension-options
+  (zipmap (map str (range))
+          (concat
+           (map (fn [[name param]]
+                  {:name name
+                   :mbql ["datetime-field" nil param]
+                   :type :type/DateTime})
+                [["Minute" "minute"]
+                 ["Minute of Hour" "minute-of-hour"]
+                 ["Hour" "hour"]
+                 ["Hour of Day" "hour-of-day"]
+                 ["Day" "day"]
+                 ["Day of Week" "day-of-week"]
+                 ["Day of Month" "day-of-month"]
+                 ["Day of Year" "day-of-year"]
+                 ["Week" "week"]
+                 ["Week of Year" "week-of-year"]
+                 ["Month" "month"]
+                 ["Month of Year" "month-of-year"]
+                 ["Quarter" "quarter"]
+                 ["Quarter of Year" "quarter-of-year"]
+                 ["Year" "year"]])
+           (map (fn [[name params]]
+                  {:name name
+                   :mbql (apply vector "binning-strategy" nil params)
+                   :type :type/Numeric})
+                [["Quantized by the default binning strategy for the field" ["default"]]
+                 ["Quantized by the 10 equally sized bins" ["num-bins" 10]]
+                 ["Quantized by the 50 equally sized bins" ["num-bins" 50]]
+                 ["Quantized by the 100 equally sized bins" ["num-bins" 100]]]))))
+
+(def ^:private dimension-options-for-response
+  (m/map-vals #(dissoc % :type) dimension-options))
+
+(def ^:private datetime-dimension-indexes
+  (->> dimension-options
+       (m/filter-kv (fn [k v] (isa? (:type v) :type/DateTime)))
+       keys
+       sort))
+
+(def ^:private numeric-dimension-indexes
+  (->> dimension-options
+       (m/filter-kv (fn [k v] (isa? (:type v) :type/Numeric)))
+       keys
+       sort))
+
+(defn- assoc-dimension-options [resp]
+  (-> resp
+      (assoc :dimension_options dimension-options-for-response)
+      (update :fields (fn [fields]
+                        (mapv (fn [{:keys [base_type] :as field}]
+                                (assoc field
+                                  :dimension_options
+                                  (cond
+
+                                    (isa? base_type :type/Number)
+                                    numeric-dimension-indexes
+
+                                    (isa? base_type :type/DateTime)
+                                    datetime-dimension-indexes
+
+                                    :else
+                                    [])))
+                              fields)))))
 
 (api/defendpoint GET "/:id/query_metadata"
   "Get metadata about a `Table` useful for running queries.
@@ -87,6 +152,7 @@
   {include_sensitive_fields (s/maybe su/BooleanString)}
   (-> (api/read-check Table id)
       (hydrate :db [:fields :target] :field_values :segments :metrics)
+      assoc-dimension-options
       (update-in [:fields] (if (Boolean/parseBoolean include_sensitive_fields)
                              ;; If someone passes include_sensitive_fields return hydrated :fields as-is
                              identity
diff --git a/test/metabase/api/table_test.clj b/test/metabase/api/table_test.clj
index 2074cad07d3..d1f1f67faa6 100644
--- a/test/metabase/api/table_test.clj
+++ b/test/metabase/api/table_test.clj
@@ -1,6 +1,7 @@
 (ns metabase.api.table-test
   "Tests for /api/table endpoints."
-  (:require [expectations :refer :all]
+  (:require [clojure.walk :as walk]
+            [expectations :refer :all]
             [metabase
              [driver :as driver]
              [http-client :as http]
@@ -23,6 +24,7 @@
             [toucan.util.test :as tt]))
 
 (resolve-private-vars metabase.models.table pk-field-id)
+(resolve-private-vars metabase.api.table dimension-options-for-response datetime-dimension-indexes numeric-dimension-indexes)
 
 
 ;; ## /api/org/* AUTHENTICATION Tests
@@ -129,10 +131,15 @@
     (perms/delete-related-permissions! (perms-group/all-users) (perms/object-path database-id))
     ((user->client :rasta) :get 403 (str "table/" table-id))))
 
+(defn- query-metadata-defaults []
+  (->> dimension-options-for-response
+       var-get
+       walk/keywordize-keys
+       (assoc (table-defaults) :dimension_options)))
 
 ;; ## GET /api/table/:id/query_metadata
 (expect
-  (merge (table-defaults)
+  (merge (query-metadata-defaults)
          (match-$ (hydrate/hydrate (Table (id :categories)) :field_values)
            {:schema       "PUBLIC"
             :name         "CATEGORIES"
@@ -151,7 +158,8 @@
                                                 :raw_column_id      $
                                                 :last_analyzed      $
                                                 :min_value          1.0
-                                                :max_value          75.0}))
+                                                :max_value          75.0
+                                                :dimension_options  (var-get numeric-dimension-indexes)}))
                              (merge defaults (match-$ (Field (id :categories :name))
                                                {:special_type       "type/Name"
                                                 :name               "NAME"
@@ -163,7 +171,8 @@
                                                 :base_type          "type/Text"
                                                 :fk_target_field_id $
                                                 :raw_column_id      $
-                                                :last_analyzed      $}))])
+                                                :last_analyzed      $
+                                                :dimension_options  []}))])
             :rows         75
             :updated_at   $
             :id           (id :categories)
@@ -194,7 +203,7 @@
 ;;; GET api/table/:id/query_metadata?include_sensitive_fields
 ;;; Make sure that getting the User table *does* include info about the password field, but not actual values themselves
 (expect
-  (merge (table-defaults)
+  (merge (query-metadata-defaults)
          (match-$ (Table (id :users))
            {:schema       "PUBLIC"
             :name         "USERS"
@@ -213,7 +222,8 @@
                                                 :raw_column_id      $
                                                 :last_analyzed      $
                                                 :min_value          1.0
-                                                :max_value          15.0}))
+                                                :max_value          15.0
+                                                :dimension_options  (var-get numeric-dimension-indexes)}))
                              (merge defaults (match-$ (Field (id :users :last_login))
                                                {:special_type       nil
                                                 :name               "LAST_LOGIN"
@@ -225,7 +235,8 @@
                                                 :visibility_type    "normal"
                                                 :fk_target_field_id $
                                                 :raw_column_id      $
-                                                :last_analyzed      $}))
+                                                :last_analyzed      $
+                                                :dimension_options  (var-get datetime-dimension-indexes)}))
                              (merge defaults (match-$ (Field (id :users :name))
                                                {:special_type       "type/Name"
                                                 :name               "NAME"
@@ -237,7 +248,8 @@
                                                 :visibility_type    "normal"
                                                 :fk_target_field_id $
                                                 :raw_column_id      $
-                                                :last_analyzed      $}))
+                                                :last_analyzed      $
+                                                :dimension_options  []}))
                              (merge defaults (match-$ (Field :table_id (id :users), :name "PASSWORD")
                                                {:special_type       "type/Category"
                                                 :name               "PASSWORD"
@@ -249,7 +261,8 @@
                                                 :visibility_type    "sensitive"
                                                 :fk_target_field_id $
                                                 :raw_column_id      $
-                                                :last_analyzed      $}))])
+                                                :last_analyzed      $
+                                                :dimension_options  []}))])
             :rows         15
             :updated_at   $
             :id           (id :users)
@@ -276,7 +289,7 @@
 ;;; GET api/table/:id/query_metadata
 ;;; Make sure that getting the User table does *not* include password info
 (expect
-  (merge (table-defaults)
+  (merge (query-metadata-defaults)
          (match-$ (Table (id :users))
            {:schema       "PUBLIC"
             :name         "USERS"
@@ -294,7 +307,8 @@
                                                 :raw_column_id      $
                                                 :last_analyzed      $
                                                 :min_value          1.0
-                                                :max_value          15.0}))
+                                                :max_value          15.0
+                                                :dimension_options  (var-get numeric-dimension-indexes)}))
                              (merge defaults (match-$ (Field (id :users :last_login))
                                                {:special_type       nil
                                                 :name               "LAST_LOGIN"
@@ -305,7 +319,8 @@
                                                 :base_type          "type/DateTime"
                                                 :fk_target_field_id $
                                                 :raw_column_id      $
-                                                :last_analyzed      $}))
+                                                :last_analyzed      $
+                                                :dimension_options  (var-get datetime-dimension-indexes)}))
                              (merge defaults (match-$ (Field (id :users :name))
                                                {:special_type       "type/Name"
                                                 :name               "NAME"
@@ -316,7 +331,8 @@
                                                 :base_type          "type/Text"
                                                 :fk_target_field_id $
                                                 :raw_column_id      $
-                                                :last_analyzed      $}))])
+                                                :last_analyzed      $
+                                                :dimension_options  []}))])
             :rows         15
             :updated_at   $
             :id           (id :users)
-- 
GitLab