diff --git a/enterprise/backend/test/metabase_enterprise/models/entity_id_test.clj b/enterprise/backend/test/metabase_enterprise/models/entity_id_test.clj index 03c5d50f27e1ed407dffe0bffaad004c97a94246..f8f5aaaee5012b21b4aefa2ed9e384613c006e6e 100644 --- a/enterprise/backend/test/metabase_enterprise/models/entity_id_test.clj +++ b/enterprise/backend/test/metabase_enterprise/models/entity_id_test.clj @@ -45,7 +45,6 @@ metabase.models.emitter.CardEmitterInstance metabase.models.emitter.DashboardEmitterInstance metabase.models.emitter.EmitterInstance - metabase.models.emitter.EmitterActionInstance metabase.models.field_values.FieldValuesInstance metabase.models.login_history.LoginHistoryInstance metabase.models.metric_important_field.MetricImportantFieldInstance diff --git a/resources/migrations/000_migrations.yaml b/resources/migrations/000_migrations.yaml index 36999093f56a4d21ace2dad7bde49f0a9bca9e5c..06d9a06d371eac572ffbe1730877d753850c99a0 100644 --- a/resources/migrations/000_migrations.yaml +++ b/resources/migrations/000_migrations.yaml @@ -12542,6 +12542,69 @@ databaseChangeLog: columnNames: action_id constraintName: pk_http_action + - changeSet: + id: v45.00-014 + author: snoe + comment: Added 0.45.0 - writeback add action_id to emitter + changes: + - addColumn: + columns: + - column: + name: action_id + type: int + remarks: The related action + tableName: emitter + + # FK constraint is added separately because deleteCascade doesn't work in addColumn -- see #14321 + - changeSet: + id: v45.00-015 + author: snoe + comment: Added 0.45.0 - writeback add fk for action_id to emitter + changes: + - addForeignKeyConstraint: + baseTableName: emitter + baseColumnNames: action_id + referencedTableName: action + referencedColumnNames: id + constraintName: fk_emitter_ref_action_id + onDelete: CASCADE + + - changeSet: + id: v45.00-016 + author: snoe + comment: Added 0.45.0 - writeback Move foreign keys from emitter_action and dashboard_emitter + changes: + - sql: + sql: >- + update emitter set + action_id = (select action_id from emitter_action where emitter_id = id); + + - changeSet: + id: v45.00-017 + author: snoe + comment: Added 0.45.0 - writeback remove any emitters that were orphaned to be safe. + changes: + - sql: + sql: delete from emitter where action_id is null; + + - changeSet: + id: v45.00-018 + author: snoe + comment: Added 0.45.0 - writeback Make emitter action_id not nullable + changes: + - addNotNullConstraint: + columnDataType: int + tableName: emitter + columnName: action_id + + - changeSet: + id: v45.00-019 + author: snoe + comment: Added 0.45.0 - writeback drop emitter_action + changes: + - dropTable: + tableName: emitter_action + # >>>>>>>>>> DO NOT ADD NEW MIGRATIONS BELOW THIS LINE! ADD THEM ABOVE <<<<<<<<<< ######################################################################################################################## diff --git a/src/metabase/api/emitter.clj b/src/metabase/api/emitter.clj index 1bbc5e752ed4854fce3be8e137c3e5ee873eacaa..abdc5f670a5842ae50ef0948817e76e7c35bed65 100644 --- a/src/metabase/api/emitter.clj +++ b/src/metabase/api/emitter.clj @@ -4,8 +4,7 @@ [metabase.actions.http-action :as http-action] [metabase.api.common :as api] [metabase.mbql.schema :as mbql.s] - [metabase.models - :refer [CardEmitter DashboardEmitter Emitter EmitterAction]] + [metabase.models :refer [CardEmitter DashboardEmitter Emitter]] [metabase.query-processor.writeback :as qp.writeback] [metabase.util :as u] [metabase.util.i18n :refer [tru]] @@ -19,9 +18,7 @@ [emitter-id] (when-let [emitter (->> (db/query {:select [:*] :from [[Emitter :emitter]] - :left-join [[EmitterAction :emitter_action] - [:= :emitter_action.emitter_id :emitter.id] - ;; TODO -- not 100% sure we need CardEmitter and DashboardEmitter, we'd + :left-join [;; TODO -- not 100% sure we need CardEmitter and DashboardEmitter, we'd ;; only need that if we wanted to hydrate the Card or Dashboard they ;; came from. [CardEmitter :card_emitter] @@ -44,8 +41,9 @@ ;; Create stuff the hard way by hand because H2 doesn't return the Emitter ID if you have Toucan `pre-insert` create ;; them for you because of some issue with INSERT RETURNING ROWS or something like that. See ;; [[metabase.models.emitter/pre-insert]] for more discussion. - (let [emitter-id (u/the-id (db/insert! Emitter {:options options, :parameter_mappings parameter_mappings}))] - (db/insert! EmitterAction {:emitter_id emitter-id, :action_id action_id}) + (let [emitter-id (u/the-id (db/insert! Emitter {:options options, + :parameter_mappings parameter_mappings + :action_id action_id}))] (cond dashboard_id (db/insert! DashboardEmitter {:emitter_id emitter-id, :dashboard_id dashboard_id}) diff --git a/src/metabase/models.clj b/src/metabase/models.clj index 8497ebb1eb97a1750b09dbdb787373ddd9b77c84..28b184c699969f4e87173d1a95a78309f0b0c749 100644 --- a/src/metabase/models.clj +++ b/src/metabase/models.clj @@ -105,7 +105,6 @@ [emitter CardEmitter] [emitter DashboardEmitter] [emitter Emitter] - [emitter EmitterAction] [field Field] [field-values FieldValues] [login-history LoginHistory] diff --git a/src/metabase/models/action.clj b/src/metabase/models/action.clj index 5546f0af655f062b8ae0ae38e41c4796a264f607..6db464fb69527951a3ddeeabd9c5ccfc10298133 100644 --- a/src/metabase/models/action.clj +++ b/src/metabase/models/action.clj @@ -132,10 +132,8 @@ [emitters] ;; emitters apparently might actually be `[nil]` (not 100% sure why) so just make sure we're not doing anything dumb ;; if this is the case. - (if-let [emitter-ids (seq (filter some? (map :id emitters)))] - (let [emitter-actions (db/select 'EmitterAction :emitter_id [:in emitter-ids]) - action-id-by-emitter-id (into {} (map (juxt :emitter_id :action_id) emitter-actions)) - actions (m/index-by :id (select-actions :id [:in (map :action_id emitter-actions)]))] + (if-let [action-id-by-emitter-id (not-empty (into {} (map (juxt :id :action_id) (filter :id emitters))))] + (let [actions-by-id (m/index-by :id (select-actions :id [:in (map val action-id-by-emitter-id)]))] (for [{emitter-id :id, :as emitter} emitters] - (some-> emitter (assoc :action (get actions (get action-id-by-emitter-id emitter-id)))))) + (some-> emitter (assoc :action (get actions-by-id (get action-id-by-emitter-id emitter-id)))))) emitters)) diff --git a/src/metabase/models/emitter.clj b/src/metabase/models/emitter.clj index 867f56be756da8856a1f9146796e89ceeab265ba..3cc604b4fc00b1d014f926a647d35f7dc238b70b 100644 --- a/src/metabase/models/emitter.clj +++ b/src/metabase/models/emitter.clj @@ -12,7 +12,6 @@ (models/defmodel CardEmitter :card_emitter) (models/defmodel DashboardEmitter :dashboard_emitter) (models/defmodel Emitter :emitter) -(models/defmodel EmitterAction :emitter_action) (defn- normalize-parameter-mappings [parameter-mappings] @@ -33,58 +32,11 @@ :options :json}) :properties (constantly {:timestamped? true})})) -(u/strict-extend - (class EmitterAction) - models/IModel - (merge models/IModelDefaults - ;; This is ok as long as we're 1:1 - {:primary-key (constantly :emitter_id)})) - -(defn- pre-insert - "We currently support two ways of creating a CardEmitter or DashboardEmitter: - - 1. Automatically create the base `Emitter` and `EmitterAction` at the same time. Pass in `:action_id` but not - `:emitter_id`. This was the original way we coded this but it actually causes a few issues since H2 doesn't seem - to return the newly created object when you do things this way - - 2. Create the base `Emitter` and `EmitterAction` by hand. Pass in `:emitter_id` but not `:action_id`. This is - probably preferable going forward because H2 does the right thing when you do it this way." - [{action-id :action_id, emitter-id :emitter_id, :as emitter}] - ;; if `emitter_id` was passed then assume the Base `Emitter` was created manually. Otherwise, create one - ;; automatically. - (let [base-emitter (if emitter-id - (db/select-one Emitter :id emitter-id) - (db/insert! Emitter (select-keys emitter [:parameter_mappings :options])))] - ;; if `action_id` was passed then automatically create an `EmitterAction`. Otherwise assume it was created manually. - (when action-id - (db/insert! EmitterAction {:action_id action-id, :emitter_id (u/the-id base-emitter)})) - ;; ok now Toucan should be able to create the `CardEmitter`/`DashboardEmitter` for us. - (-> emitter - (select-keys [:dashboard_id :card_id]) - (assoc :emitter_id (u/the-id base-emitter))))) - -(defn- pre-update - [emitter] - (when-some [base-emitter (not-empty (select-keys emitter [:parameter_mappings :options]))] - (db/update! Emitter (:emitter_id emitter) base-emitter)) - (when-some [emitter-action (-> emitter - (select-keys [:action_id]) - not-empty)] - (db/update! EmitterAction (:emitter_id emitter) emitter-action)) - (not-empty (select-keys emitter [:emitter_id :dashboard_id :card_id]))) - -(defn- pre-delete - [emitter] - (db/delete! Emitter :id (:emitter_id emitter)) - emitter) - (def ^:private Emitter-subtype-IModel-impl "[[models/IModel]] impl for `DashboardEmitter` and `CardEmitter`" (merge models/IModelDefaults - {:primary-key (constantly :emitter_id) ; This is ok as long as we're 1:1 - :pre-delete pre-delete - :pre-update pre-update - :pre-insert pre-insert})) + ; This is ok as long as we're 1:1 + {:primary-key (constantly :emitter_id)})) (u/strict-extend (class DashboardEmitter) models/IModel @@ -107,6 +59,7 @@ (let [qualified-join-column (keyword (str "typed_emitter." (name join-column))) emitters (->> {:select [qualified-join-column :emitter.id + :emitter.action_id :emitter.options :emitter.parameter_mappings :emitter.created_at diff --git a/test/metabase/actions/test_util.clj b/test/metabase/actions/test_util.clj index e2058cf17b91246f10e868913aee4e5d1e9be632..f8860662d66864dfa425ada7991edfdbb41c417c 100644 --- a/test/metabase/actions/test_util.clj +++ b/test/metabase/actions/test_util.clj @@ -5,7 +5,7 @@ [metabase.driver.sql-jdbc.connection :as sql-jdbc.conn] [metabase.http-client :as client] [metabase.models :refer [Action Card CardEmitter Dashboard DashboardEmitter - Database Emitter EmitterAction QueryAction]] + Database Emitter QueryAction]] [metabase.models.action :as action] [metabase.test :as mt] [metabase.test.data.dataset-definitions :as defs] @@ -168,7 +168,8 @@ (let [parent-model (db/resolve-model card-or-dashboard-model)] (mt/with-temp* [parent-model [{emitter-parent-id :id}] Emitter [{emitter-id :id} {:parameter_mappings {"my_id" [:variable [:template-tag "id"]] - "my_fail" [:variable [:template-tag "fail"]]}}]] + "my_fail" [:variable [:template-tag "fail"]]} + :action_id action-id}]] (testing "Sanity check: emitter-id should be non-nil" (is (integer? emitter-id))) (testing "Sanity check: make sure parameter mappings were defined the way we'd expect" @@ -177,8 +178,6 @@ (db/select-one-field :parameter_mappings Emitter :id emitter-id)))) ;; these are tied to the Card or Dashboad and Emitter above and will get cascade deleted. We can't use `with-temp*` for them ;; because it doesn't seem to work with tables with compound PKs - (db/insert! EmitterAction {:emitter_id emitter-id - :action_id action-id}) (condp = (type parent-model) (type Card) (db/insert! CardEmitter {:card_id emitter-parent-id diff --git a/test/metabase/api/card_test.clj b/test/metabase/api/card_test.clj index 59259b349001b656a6c36d6068ee01f0e622dc98..ff3880933dc000de54f274840df6ebb9737dfe38 100644 --- a/test/metabase/api/card_test.clj +++ b/test/metabase/api/card_test.clj @@ -19,6 +19,7 @@ Collection Dashboard Database + Emitter ModerationReview PersistedInfo Pulse @@ -754,8 +755,9 @@ (testing "GET /api/card/:id" (testing "Fetch card with an emitter" (mt/with-temp* [Card [read-card {:name "Test Read Card"}] - Card [write-card {:is_write true :name "Test Write Card"}]] - (db/insert! CardEmitter {:action_id (u/the-id (db/select-one-field :action_id QueryAction :card_id (u/the-id write-card))) + Card [write-card {:is_write true :name "Test Write Card"}] + Emitter [{emitter-id :id} {:action_id (u/the-id (db/select-one-field :action_id QueryAction :card_id (u/the-id write-card)))}]] + (db/insert! CardEmitter {:emitter_id emitter-id :card_id (u/the-id read-card)}) (testing "admin sees emitters" (is (partial= diff --git a/test/metabase/api/dashboard_test.clj b/test/metabase/api/dashboard_test.clj index b7008af020cd5cdc3ac23499a5a93b00fc38f692..2159615ba9032af0e63f37c6722990a6b21f8bc4 100644 --- a/test/metabase/api/dashboard_test.clj +++ b/test/metabase/api/dashboard_test.clj @@ -15,6 +15,7 @@ DashboardCard DashboardCardSeries DashboardEmitter + Emitter Field FieldValues Pulse @@ -345,8 +346,9 @@ (testing "GET /api/dashboard/:id" (testing "Fetch dashboard with an emitter" (mt/with-temp* [Dashboard [dashboard {:name "Test Dashboard"}] - Card [write-card {:is_write true :name "Test Write Card"}]] - (db/insert! DashboardEmitter {:action_id (u/the-id (db/select-one-field :action_id QueryAction :card_id (u/the-id write-card))) + Card [write-card {:is_write true :name "Test Write Card"}] + Emitter [{emitter-id :id} {:action_id (u/the-id (db/select-one-field :action_id QueryAction :card_id (u/the-id write-card)))}]] + (db/insert! DashboardEmitter {:emitter_id emitter-id :dashboard_id (u/the-id dashboard)}) (testing "admin sees emitters" (is (partial= diff --git a/test/metabase/cmd/copy_test.clj b/test/metabase/cmd/copy_test.clj index 2173f8a3c9492dd622bc793f75cf16d93dce8e57..f420566e167404f1b8c37551973333bb8eb94827 100644 --- a/test/metabase/cmd/copy_test.clj +++ b/test/metabase/cmd/copy_test.clj @@ -26,7 +26,7 @@ (let [migrated-model-names (set (map :name copy/entities)) ;; Models that should *not* be migrated in `load-from-h2`. models-to-exclude #{"TaskHistory" "Query" "QueryCache" "QueryExecution" "CardFavorite" "DashboardFavorite" - "Action" "HTTPAction" "QueryAction" "CardEmitter" "DashboardEmitter" "EmitterAction"} + "Action" "HTTPAction" "QueryAction" "CardEmitter" "DashboardEmitter"} all-model-names (set (for [ns u/metabase-namespace-symbols :when (or (re-find #"^metabase\.models\." (name ns)) (= (name ns) "metabase.db.data-migrations"))