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)) | |