Code related to adding the Sample Database on launch, or adding it back programmatically (used by the REST API). | (ns metabase.sample-data (:require [clojure.java.io :as io] [clojure.string :as str] [metabase.plugins :as plugins] [metabase.sync.core :as sync] [metabase.util.files :as u.files] [metabase.util.i18n :refer [trs]] [metabase.util.log :as log] [ring.util.codec :as codec] [toucan2.core :as t2]) (:import (java.net URL))) |
(set! *warn-on-reflection* true) | |
(def ^:private ^String sample-database-name "Sample Database") (def ^:private ^String sample-database-filename "sample-database.db.mv.db") | |
Reuse the plugins directory for the destination to extract the sample database because it's pretty much guaranteed to exist and be writable. | (defn- target-path [] (u.files/append-to-path (plugins/plugins-dir) sample-database-filename)) |
(defn- process-sample-db-path
[base-path]
(-> base-path
(str/replace #"\.mv\.db$" ) ; strip the .mv.db suffix from the path
codec/url-decode ; for some reason the path can get URL-encoded so we decode it here
(str ";USER=GUEST;PASSWORD=guest"))) ; specify the GUEST user account created for the DB | |
(defn- jar-db-details
[^URL resource]
(-> (.getPath resource)
(str/replace #"^file:" "zip:") ; to connect to an H2 DB inside a JAR just replace file: with zip: (this doesn't
; do anything when running from the Clojure CLI, which has no `file:` prefix)
process-sample-db-path)) | |
(defn- extract-sample-database!
[]
(u.files/with-open-path-to-resource [sample-db-path sample-database-filename]
(let [dest-path (target-path)]
(u.files/copy-file! sample-db-path dest-path)
(-> (str "file:" dest-path)
process-sample-db-path)))) | |
Tries to extract the sample database out of the JAR (for performance) and then returns a db-details map containing a path to the copied database. | (defn- try-to-extract-sample-database!
[]
(let [resource (io/resource sample-database-filename)]
(when-not resource
(throw (Exception. (trs "Sample database DB file ''{0}'' cannot be found."
sample-database-filename))))
{:db
(if-not (:temp (plugins/plugins-dir-info))
(extract-sample-database!)
(do
;; If the plugins directory is a temp directory, fall back to reading the DB directly from the JAR until a
;; working plugins directory is available. (We want to ensure the sample DB is in a stable location.)
(log/warn (str "Sample database could not be extracted to the plugins directory,"
"which may result in slow startup times. "
"Please set MB_PLUGINS_DIR to a writable directory and restart Metabase."))
(jar-db-details resource)))})) |
Adds the sample database as a Metabase DB if it doesn't already exist. If it does exist in the app DB, we update its details. | (defn extract-and-sync-sample-database!
[]
(try
(log/info "Loading sample database")
(let [details (try-to-extract-sample-database!)
db (if (t2/exists? :model/Database :is_sample true)
(t2/select-one :model/Database (first (t2/update-returning-pks! :model/Database :is_sample true {:details details})))
(first (t2/insert-returning-instances! :model/Database
:name sample-database-name
:details details
:engine :h2
:is_sample true)))]
(log/debug "Syncing Sample Database...")
(sync/sync-database! db))
(log/debug "Finished adding Sample Database.")
(catch Throwable e
(log/error e "Failed to load sample database")))) |
Update the path to the sample database DB if it exists in case the JAR has moved. | (defn update-sample-database-if-needed!
([]
(update-sample-database-if-needed! (t2/select-one :model/Database :is_sample true)))
([sample-db]
(when sample-db
(let [intended (try-to-extract-sample-database!)]
(when (not= (:details sample-db) intended)
(t2/update! :model/Database (:id sample-db) {:details intended})))))) |