/api/notify/* endpoints which receive inbound etl server notifications. | (ns metabase.api.notify (:require [clojure.string :as str] [compojure.core :refer [POST]] [metabase.api.common :as api] [metabase.driver :as driver] [metabase.driver.util :as driver.u] [metabase.models.database :refer [Database]] [metabase.models.table :refer [Table]] [metabase.sync :as sync] [metabase.sync.sync-metadata :as sync-metadata] [metabase.sync.sync-metadata.tables :as sync-tables] [metabase.sync.util :as sync-util] [metabase.util.i18n :refer [trs]] [metabase.util.malli.schema :as ms] [toucan2.core :as t2])) |
(set! *warn-on-reflection* true) | |
/db/:id | (api/defendpoint POST "Notification about a potential schema change to one of our `Databases`. Caller can optionally specify a `:table_id` or `:table_name` in the body to limit updates to a single `Table`. Optional Parameter `:scan` can be `\"full\"` or `\"schema\"` for a full sync or a schema sync, available regardless if a `:table_id` or `:table_name` is passed. This endpoint is secured by an API key that needs to be passed as a `X-METABASE-APIKEY` header which needs to be defined in the `MB_API_KEY` [environment variable](https://www.metabase.com/docs/latest/configuring-metabase/environment-variables.html#mb_api_key)" [id :as {{:keys [table_id table_name scan synchronous?]} :body}] {id ms/PositiveInt table_id [:maybe ms/PositiveInt] table_name [:maybe ms/NonBlankString] scan [:maybe [:enum "full" "schema"]]} (let [schema? (when scan (#{"schema" :schema} scan)) table-sync-fn (if schema? sync-metadata/sync-table-metadata! sync/sync-table!) db-sync-fn (if schema? sync-metadata/sync-db-metadata! sync/sync-database!)] (api/let-404 [database (t2/select-one Database :id id)] (let [table (cond table_id (api/check-404 (t2/select-one Table :db_id id, :id (int table_id))) table_name (api/check-404 (t2/select-one Table :db_id id, :name table_name)))] (cond-> (future (if table (table-sync-fn table) (db-sync-fn database))) synchronous? deref)))) {:success true}) |
(defn- without-stacktrace [^Throwable throwable] (doto throwable (.setStackTrace (make-array StackTraceElement 0)))) | |
(defn- find-and-sync-new-table [database table-name schema-name] (let [driver (driver.u/database->driver database) {db-tables :tables} (driver/describe-database driver database)] (if-let [table (some (fn [table-in-db] (when (and (= schema-name (:schema table-in-db)) (= table-name (:name table-in-db))) table-in-db)) db-tables)] (let [created (sync-tables/create-or-reactivate-table! database table)] (doto created sync/sync-table! sync-util/set-initial-table-sync-complete!)) (throw (without-stacktrace (ex-info (trs "Unable to identify table ''{0}.{1}''" schema-name table-name) {:status-code 404 :schema_name schema-name :table_name table-name})))))) | |
/db/attached_datawarehouse | (api/defendpoint POST "Sync the attached datawarehouse. Can provide in the body: - table_name and schema_name: both strings. Will look for an existing table and sync it, otherwise will try to find a new table with that name and sync it. If it cannot find a table it will throw an error. If table_name is empty or blank, will sync the entire database. - synchronous?: is a boolean value to indicate if this should block on the result." [:as {{:keys [table_name schema_name synchronous?]} :body}] {table_name [:maybe ms/NonBlankString] schema_name [:maybe string?] synchronous? [:maybe ms/BooleanValue]} (api/let-404 [database (t2/select-one :model/Database :is_attached_dwh true)] (if (str/blank? table_name) (cond-> (future (sync-metadata/sync-db-metadata! database)) synchronous? deref) (if-let [table (t2/select-one Table :db_id (:id database), :name table_name :schema schema_name)] (cond-> (future (sync-metadata/sync-table-metadata! table)) synchronous? deref) ;; find and sync is always synchronous. And we want it to be so since the "can't find this table" error is ;; rather informative. If it's on a future we won't see it. (find-and-sync-new-table database table_name schema_name)))) {:success true}) |
/db/:id/new-table | (api/defendpoint POST "Sync a new table without running a full database sync. Requires `schema_name` and `table_name`. Will throw an error if the table already exists in Metabase or cannot be found." [id :as {{:keys [schema_name table_name]} :body}] {id ms/PositiveInt schema_name ms/NonBlankString table_name ms/NonBlankString} (api/let-404 [database (t2/select-one Database :id id)] (if-not (t2/select-one Table :db_id id :name table_name :schema schema_name) (find-and-sync-new-table database table_name schema_name) (throw (without-stacktrace (ex-info (trs "Table ''{0}.{1}'' already exists" schema_name table_name) {:status-code 400 :schema_name schema_name :table_name table_name})))))) |
(api/define-routes) | |