Implementation for a delayed-load driver that implements a few basic driver methods ( See https://github.com/metabase/metabase/wiki/Metabase-Plugin-Manifest-Reference for all the options allowed for a plugin manifest. | (ns metabase.plugins.lazy-loaded-driver (:require [clojure.java.io :as io] [metabase.driver :as driver] [metabase.driver.common :as driver.common] [metabase.plugins.init-steps :as init-steps] [metabase.util :as u] [metabase.util.i18n :refer [trs]] [metabase.util.log :as log] [metabase.util.yaml :as yaml]) (:import (clojure.lang MultiFn))) |
(set! *warn-on-reflection* true) | |
(defn- parse-connection-property [prop]
(cond
(string? prop)
(or (driver.common/default-options (keyword prop))
(driver.common/default-connection-info-fields (keyword prop))
(throw (Exception. (trs "Default connection property {0} does not exist." prop))))
(not (map? prop))
(throw (Exception. (trs "Invalid connection property {0}: not a string or map." prop)))
(:merge prop)
(into {} (map parse-connection-property) (:merge prop))
:else
prop)) | |
Parse the connection properties included in the plugin manifest. These can be one of several things -- a key
referring to one of the default maps in | (defn- parse-connection-properties
[{:keys [connection-properties]}]
(->> (map parse-connection-property connection-properties)
(map u/one-or-many)
(apply concat))) |
(defn- make-initialize! [driver add-to-classpath! init-steps]
(fn [_]
;; First things first: add the driver to the classpath!
(when add-to-classpath!
(add-to-classpath!))
;; remove *this* implementation of `initialize!`, because as you will see below, we want to give
;; lazy-load drivers the option to implement `initialize!` and do other things, which means we need to
;; manually call it. When we do so we don't want to get stuck in an infinite loop of calls back to this
;; implementation
(remove-method driver/initialize! driver)
;; ok, do the init steps listed in the plugin mainfest
(u/profile (u/format-color 'magenta (trs "Load lazy loading driver {0}" driver))
(init-steps/do-init-steps! init-steps))
;; ok, now go ahead and call `driver/initialize!` a second time on the driver in case it actually has
;; an implementation of `initialize!` other than this one. If it does not, we'll just end up hitting
;; the default implementation, which is a no-op
(driver/initialize! driver))) | |
Register a basic shell of a Metabase driver using the information from its Metabase plugin | (defn register-lazy-loaded-driver!
[{:keys [add-to-classpath!]
init-steps :init
contact-info :contact-info
superseded-by :superseded-by
{driver-name :name, :keys [abstract display-name parent], :or {abstract false}, :as driver-info} :driver}]
{:pre [(map? driver-info)]}
(let [driver (keyword driver-name)
connection-props (parse-connection-properties driver-info)]
;; Make sure the driver has required properties like driver-name
(when-not (seq driver-name)
(throw (ex-info (trs "Cannot initialize plugin: missing required property `driver-name`")
driver-info)))
;; if someone forgot to include connection properties for a non-abstract driver throw them a bone and warn them
;; about it
(when (and (not abstract)
(empty? connection-props))
(log/warn (u/format-color :red "Warning: plugin manifest for %s does not include connection properties" driver)))
;; ok, now add implementations for the so-called "non-trivial" driver multimethods
(doseq [[^MultiFn multifn, f]
{driver/initialize! (make-initialize! driver add-to-classpath! init-steps)
driver/display-name (when display-name (constantly display-name))
driver/contact-info (constantly contact-info)
driver/connection-properties (constantly connection-props)
driver/superseded-by (constantly (keyword superseded-by))}]
(when f
(.addMethod multifn driver f)))
;; finally, register the Metabase driver
(log/debug (u/format-color :magenta "Registering lazy loading driver %s..." driver))
(driver/register! driver, :parent (set (map keyword (u/one-or-many parent))), :abstract? abstract))) |
(defn- load-connection-properties
[driver]
(let [manifest (str (io/file "modules/drivers/" (name driver) "resources/metabase-plugin.yaml"))
properties (some->
(slurp manifest)
yaml/parse-string
:driver
(parse-connection-properties))]
(.addMethod ^MultiFn driver/connection-properties driver (constantly properties)))) | |
(comment (load-connection-properties :snowflake)) | |