Logic for constructing a map of metadata from the Metabase application database that matches the form of DB metadata
about Fields in a Table, and for fetching the DB metadata itself. This metadata is used by the logic in other
| (ns metabase.sync.sync-metadata.fields.our-metadata (:require [medley.core :as m] [metabase.models.table :as table] [metabase.sync.interface :as i] [metabase.sync.sync-metadata.fields.common :as common] [metabase.util :as u] [metabase.util.malli :as mu] [toucan2.core :as t2])) |
+----------------------------------------------------------------------------------------------------------------+ | FETCHING OUR CURRENT METADATA | +----------------------------------------------------------------------------------------------------------------+ | |
(mu/defn- fields->parent-id->fields :- [:map-of common/ParentID [:set common/TableMetadataFieldWithID]]
[fields :- [:maybe [:sequential i/FieldInstance]]]
(->> (for [field fields]
{:parent-id (:parent_id field)
:id (:id field)
:name (:name field)
:database-type (:database_type field)
:effective-type (:effective_type field)
:coercion-strategy (:coercion_strategy field)
:base-type (:base_type field)
:semantic-type (:semantic_type field)
:pk? (isa? (:semantic_type field) :type/PK)
:field-comment (:description field)
:json-unfolding (:json_unfolding field)
:database-is-auto-increment (:database_is_auto_increment field)
:position (:position field)
:database-position (:database_position field)
:database-partitioned (:database_partitioned field)
:database-required (:database_required field)})
;; make a map of parent-id -> set of child Fields
(group-by :parent-id)
;; remove the parent ID because the Metadata from `describe-table` won't have it. Save the results as a set
(m/map-vals (fn [fields]
(set (for [field fields]
(dissoc field :parent-id))))))) | |
(mu/defn- add-nested-fields :- common/TableMetadataFieldWithID
"Recursively add entries for any nested-fields to `field`."
[metabase-field :- common/TableMetadataFieldWithID
parent-id->fields :- [:map-of common/ParentID [:set common/TableMetadataFieldWithID]]]
(let [nested-fields (get parent-id->fields (u/the-id metabase-field))]
(if-not (seq nested-fields)
metabase-field
(assoc metabase-field :nested-fields (set (for [nested-field nested-fields]
(add-nested-fields nested-field parent-id->fields))))))) | |
(mu/defn fields->our-metadata :- [:set common/TableMetadataFieldWithID]
"Given a sequence of Metabase Fields, format them and return them in a hierachy so the format matches the one
`db-metadata` comes back in."
([fields :- [:maybe [:sequential i/FieldInstance]]]
(fields->our-metadata fields nil))
([fields :- [:maybe [:sequential i/FieldInstance]], top-level-parent-id :- common/ParentID]
(let [parent-id->fields (fields->parent-id->fields fields)]
;; get all the top-level fields, then call `add-nested-fields` to recursively add the fields
(set (for [metabase-field (get parent-id->fields top-level-parent-id)]
(add-nested-fields metabase-field parent-id->fields)))))) | |
(mu/defn- table->fields :- [:maybe [:sequential i/FieldInstance]]
"Fetch active Fields from the Metabase application database for a given `table`."
[table :- i/TableInstance]
(t2/select [:model/Field :name :database_type :base_type :effective_type :coercion_strategy :semantic_type
:parent_id :id :description :database_position :nfc_path :database_is_auto_increment :database_required
:database_partitioned :json_unfolding :position]
:table_id (u/the-id table)
:active true
{:order-by table/field-order-rule})) | |
(mu/defn our-metadata :- [:set common/TableMetadataFieldWithID] "Return information we have about Fields for a `table` in the application database in (almost) exactly the same `TableMetadataField` format returned by `describe-table`." [table :- i/TableInstance] (-> table table->fields fields->our-metadata)) | |