Logic for updating metadata properties of | (ns metabase.sync.sync-metadata.fields.sync-metadata (:require [clojure.string :as str] [metabase.sync.interface :as i] [metabase.sync.sync-metadata.fields.common :as common] [metabase.sync.util :as sync-util] [metabase.util :as u] [metabase.util.log :as log] [metabase.util.malli :as mu] [metabase.util.malli.schema :as ms] [toucan2.core :as t2])) |
(mu/defn- update-field-metadata-if-needed! :- [:enum 0 1]
"Update the metadata for a Metabase Field as needed if any of the info coming back from the DB has changed. Syncs
base type, database type, semantic type, and comments/remarks; returns `1` if the Field was updated; `0` otherwise."
[table :- i/TableInstance
field-metadata :- i/TableMetadataField
metabase-field :- common/TableMetadataFieldWithID]
(let [{old-database-type :database-type
old-base-type :base-type
old-field-comment :field-comment
old-semantic-type :semantic-type
old-database-position :database-position
old-position :position
old-database-name :name
old-database-is-auto-increment :database-is-auto-increment
old-db-partitioned :database-partitioned
old-db-required :database-required} metabase-field
{new-database-type :database-type
new-base-type :base-type
new-field-comment :field-comment
new-database-position :database-position
new-database-name :name
new-database-is-auto-increment :database-is-auto-increment
new-db-partitioned :database-partitioned
new-db-required :database-required} field-metadata
new-database-is-auto-increment (boolean new-database-is-auto-increment)
new-db-required (boolean new-db-required)
new-database-type (or new-database-type "NULL")
new-semantic-type (common/semantic-type field-metadata)
new-db-type?
(not= old-database-type new-database-type)
new-base-type?
(not= old-base-type new-base-type)
;; only sync comment if old value was blank so we don't overwrite user-set values
new-semantic-type?
(and (nil? old-semantic-type)
(not= old-semantic-type new-semantic-type))
new-comment?
(and (str/blank? old-field-comment)
(not (str/blank? new-field-comment)))
new-database-position?
(not= old-database-position new-database-position)
;; these fields are paired by by metabase.sync.sync-metadata.fields.common/canonical-name, so if they are
;; different they have the same canonical representation (lower-casing at the moment).
new-name? (not= old-database-name new-database-name)
new-db-auto-incremented? (not= old-database-is-auto-increment new-database-is-auto-increment)
new-db-partitioned? (not= new-db-partitioned old-db-partitioned)
new-db-required? (not= old-db-required new-db-required)
;; calculate combined updates
updates
(merge
(when new-db-type?
(log/infof "Database type of %s has changed from '%s' to '%s'."
(common/field-metadata-name-for-logging table metabase-field)
old-database-type
new-database-type)
{:database_type new-database-type})
(when new-base-type?
(log/infof "Base type of %s has changed from '%s' to '%s'. This field will be refingerprinted and analyzed."
(common/field-metadata-name-for-logging table metabase-field)
old-base-type
new-base-type)
{:base_type new-base-type
:effective_type new-base-type
:coercion_strategy nil
;; reset fingerprint version so this field will get re-fingerprinted and analyzed
:fingerprint_version 0
:fingerprint nil
;; semantic type needs to be set to nil so that the fingerprinter can re-infer it during analysis
:semantic_type nil})
(when new-semantic-type?
(log/infof "Semantic type of %s has changed from '%s' to '%s'."
(common/field-metadata-name-for-logging table metabase-field)
old-semantic-type
new-semantic-type)
{:semantic_type new-semantic-type})
(when new-comment?
(log/infof "Comment has been added for %s."
(common/field-metadata-name-for-logging table metabase-field))
{:description new-field-comment})
(when new-database-position?
(log/infof "Database position of %s has changed from '%s' to '%s'."
(common/field-metadata-name-for-logging table metabase-field)
old-database-position
new-database-position)
{:database_position new-database-position})
(when (and (= (:field_order table) :database)
(not= old-position new-database-position))
(log/infof "Position of %s has changed from '%s' to '%s'."
(common/field-metadata-name-for-logging table metabase-field)
old-position
new-database-position)
{:position new-database-position})
(when new-name?
(log/infof "Name of %s has changed from '%s' to '%s'."
(common/field-metadata-name-for-logging table metabase-field)
old-database-name
new-database-name)
{:name new-database-name})
(when new-db-auto-incremented?
(log/infof "Database auto incremented of %s has changed from '%s' to '%s'."
(common/field-metadata-name-for-logging table metabase-field)
old-database-is-auto-increment
new-database-is-auto-increment)
{:database_is_auto_increment new-database-is-auto-increment})
(when new-db-partitioned?
(log/infof "Database partitioned of %s has changed from '%s' to '%s'."
(common/field-metadata-name-for-logging table metabase-field)
old-db-partitioned
new-db-partitioned)
{:database_partitioned new-db-partitioned})
(when new-db-required?
(log/infof "Database required of %s has changed from '%s' to '%s'."
(common/field-metadata-name-for-logging table metabase-field)
old-db-required
new-db-required)
{:database_required new-db-required}))]
;; if any updates need to be done, do them and return 1 (because 1 Field was updated), otherwise return 0
(if (and (seq updates)
(pos? (t2/update! :model/Field (u/the-id metabase-field) updates)))
1
0))) | |
(declare update-metadata!) | |
(mu/defn- update-nested-fields-metadata! :- ms/IntGreaterThanOrEqualToZero
"Recursively call `update-metadata!` for all the nested Fields in a `metabase-field`."
[table :- i/TableInstance
field-metadata :- i/TableMetadataField
metabase-field :- common/TableMetadataFieldWithID]
(let [nested-fields-metadata (:nested-fields field-metadata)
metabase-nested-fields (:nested-fields metabase-field)]
(if (seq metabase-nested-fields)
(update-metadata! table (set nested-fields-metadata) (set metabase-nested-fields))
0))) | |
(mu/defn update-metadata! :- ms/IntGreaterThanOrEqualToZero
"Make sure things like PK status and base-type are in sync with what has come back from the DB. Recursively updates
nested Fields. Returns total number of Fields updated."
[table :- i/TableInstance
db-metadata :- [:set i/TableMetadataField]
our-metadata :- [:set common/TableMetadataFieldWithID]]
(sync-util/sum-for [metabase-field our-metadata]
;; only update metadata for 'existing' Fields that are present in our Metadata (i.e., present in the application
;; database) and that are still considered active (i.e., present in DB metadata)
(when-let [field-metadata (common/matching-field-metadata metabase-field db-metadata)]
(+ (update-field-metadata-if-needed! table field-metadata metabase-field)
(update-nested-fields-metadata! table field-metadata metabase-field))))) | |