From d2fcd77f2eafe0979eb65e4d7113a5a0cb2aee29 Mon Sep 17 00:00:00 2001
From: Cam Saul <cam@getluckybird.com>
Date: Wed, 13 May 2015 16:45:52 -0700
Subject: [PATCH] :heart_eyes_cat: Add support for new-style connection details
 maps for Postgres to Clojure backend. Add some extra unit tests for funsies

---
 .dir-locals.el                         |  1 +
 src/metabase/driver/postgres.clj       | 33 ++++++++----
 test/metabase/driver/postgres_test.clj | 70 ++++++++++++++++++++++++++
 3 files changed, 95 insertions(+), 9 deletions(-)
 create mode 100644 test/metabase/driver/postgres_test.clj

diff --git a/.dir-locals.el b/.dir-locals.el
index 6ef64b82e46..c633e019cd1 100644
--- a/.dir-locals.el
+++ b/.dir-locals.el
@@ -31,6 +31,7 @@
                               (org-perms-case 1)
                               (pdoseq 1)
                               (qp-expect-with-all-drivers 1)
+                              (resolve-private-fns 1)
                               (symbol-macrolet 1)
                               (sync-in-context 2)
                               (upd 2)
diff --git a/src/metabase/driver/postgres.clj b/src/metabase/driver/postgres.clj
index 3b03dd8c407..70fa2046758 100644
--- a/src/metabase/driver/postgres.clj
+++ b/src/metabase/driver/postgres.clj
@@ -90,19 +90,31 @@
       (rename-keys {:dbname :db})
       kdb/postgres))
 
-(defn- database->connection-details [database]
-  (let [details (-<>> database :details :conn_str             ; get conn str like "password=corvus user=corvus ..."
-                      (s/split <> #" ")                       ; split into k=v pairs
-                      (map (fn [pair]                          ; convert to {:k v} pairs
-                             (let [[k v] (s/split pair #"=")]
-                               {(keyword k) v})))
-                      (reduce conj {}))                       ; combine into single dict
-        {:keys [host port]} details]
+(defn- is-legacy-conn-details?
+  "Is DETAILS-MAP a legacy map (i.e., does it only contain `conn_str`)?"
+  [details-map]
+  {:pre [(map? details-map)]}
+  (not (:dbname details-map)))
+
+(defn- parse-legacy-conn-str
+  "Parse a legacy `database.details.conn_str` CONNECTION-STRING."
+  [connection-string]
+  {:pre [(string? connection-string)]}
+  (-<>> connection-string
+        (s/split <> #" ")               ; split into k=v pairs
+        (map (fn [pair]                  ; convert to {:k v} pairs
+               (let [[k v] (s/split pair #"=")]
+                 {(keyword k) v})))
+        (reduce conj {})))
+
+(defn- database->connection-details [{:keys [details]}]
+  (let [{:keys [host port] :as details} (if (is-legacy-conn-details? details) (parse-legacy-conn-str (:conn_str details))
+                                            details)]
     (-> details
         (assoc :host       host
                :make-pool? false
                :db-type    :postgres                          ; What purpose is this serving?
-               :ssl        (:ssl (:details database))
+               :ssl        (:ssl details)
                :port       (Integer/parseInt port))
         (rename-keys {:dbname :db}))))
 
@@ -122,3 +134,6 @@
     :database->connection-details        database->connection-details
     :sql-string-length-fn                :CHAR_LENGTH
     :timezone->set-timezone-sql          timezone->set-timezone-sql}))
+
+(use 'metabase.db)
+(use 'metabase.models.database)
diff --git a/test/metabase/driver/postgres_test.clj b/test/metabase/driver/postgres_test.clj
new file mode 100644
index 00000000000..5defacf9d29
--- /dev/null
+++ b/test/metabase/driver/postgres_test.clj
@@ -0,0 +1,70 @@
+(ns metabase.driver.postgres-test
+  (:require [expectations :refer :all]
+            [metabase.driver.postgres :refer :all]
+            [metabase.test.util :refer [resolve-private-fns]]))
+
+(resolve-private-fns metabase.driver.postgres
+  connection-details->connection-spec
+  database->connection-details)
+
+;; # Check that database->connection details still works whether we're dealing with new-style or legacy details
+;; ## new-style
+(expect {:db "bird_sightings"
+         :db-type :postgres
+         :make-pool? false
+         :ssl false
+         :port 5432
+         :host "localhost"
+         :user "camsaul"}
+  (database->connection-details {:details {:ssl    false
+                                           :host   "localhost"
+                                           :port   "5432"
+                                           :dbname "bird_sightings"
+                                           :user   "camsaul"}}))
+
+;; ## legacy
+(expect {:db "bird_sightings"
+         :ssl nil
+         :db-type :postgres
+         :make-pool? false
+         :user "camsaul"
+         :port 5432
+         :host "localhost"}
+  (database->connection-details {:details {:conn_str "host=localhost port=5432 dbname=bird_sightings user=camsaul"}}))
+
+
+;; # Check that SSL params get added the connection details in the way we'd like
+;; ## no SSL -- this should *not* include the key :ssl (regardless of its value) since that will cause the PG driver to use SSL anyway
+(expect
+    {:user "camsaul"
+     :host "localhost"
+     :port "5432"
+     :db "bird_sightings"
+     :classname "org.postgresql.Driver"
+     :subprotocol "postgresql"
+     :subname "//localhost:5432/bird_sightings"
+     :make-pool? true}
+  (connection-details->connection-spec {:ssl    false
+                                        :host   "localhost"
+                                        :port   "5432"
+                                        :dbname "bird_sightings"
+                                        :user   "camsaul"}))
+
+;; ## ssl - check that expected params get added
+(expect
+    {:ssl true
+     :make-pool? true
+     :db "bird_sightings"
+     :sslmode "require"
+     :classname "org.postgresql.Driver"
+     :port "5432"
+     :subprotocol "postgresql"
+     :host "localhost"
+     :user "camsaul"
+     :sslfactory "org.postgresql.ssl.NonValidatingFactory"
+     :subname "//localhost:5432/bird_sightings"}
+  (connection-details->connection-spec {:ssl    true
+                                        :host   "localhost"
+                                        :port   "5432"
+                                        :dbname "bird_sightings"
+                                        :user   "camsaul"}))
-- 
GitLab