Skip to content
Snippets Groups Projects
Unverified Commit 78053254 authored by Alexander Polyankin's avatar Alexander Polyankin Committed by GitHub
Browse files

Move loading of linked targets metadata to the BE (#43380)

parent 3b6def1e
No related branches found
No related tags found
No related merge requests found
......@@ -141,10 +141,6 @@ function setup() {
});
createTargetDashboard().then(targetDashboardId => {
cy.intercept("GET", `/api/dashboard/${targetDashboardId}`).as(
"targetDashboardLoaded",
);
cy.wrap(targetDashboardId).as("targetDashboardId");
// Create a click behaviour for the question card
......@@ -289,7 +285,6 @@ function createTargetDashboard() {
function visitSourceDashboard() {
cy.get("@sourceDashboardId").then(id => {
visitDashboard(id);
cy.wait("@targetDashboardLoaded");
});
}
......
......@@ -877,16 +877,17 @@ describe("scenarios > dashboard", () => {
],
});
cy.intercept("GET", `/api/dashboard/${NEW_DASHBOARD_ID}`).as(
"loadDashboard",
);
cy.intercept(
"GET",
`/api/dashboard/${ORDERS_DASHBOARD_ID}/query_metadata`,
).as("queryMetadata");
});
},
);
cy.signInAsNormalUser();
visitDashboard(ORDERS_DASHBOARD_ID);
cy.wait("@loadDashboard");
cy.wait("@queryMetadata");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
cy.findByText("Orders in a dashboard");
// eslint-disable-next-line no-unscoped-text-selectors -- deprecated usage
......
import { denormalize, normalize, schema } from "normalizr";
import {
loadMetadataForDashcards,
loadMetadataForLinkedTargets,
} from "metabase/dashboard/actions/metadata";
import { loadMetadataForDashcards } from "metabase/dashboard/actions/metadata";
import {
getDashboardById,
getDashCardById,
......@@ -180,12 +177,7 @@ export const fetchDashboardCardMetadata = createAsyncThunk(
async (dashboard: Dashboard, { dispatch }) => {
const dashboardType = getDashboardType(dashboard.id);
if (dashboardType === "normal") {
await dispatch(
Dashboards.actions.fetchMetadata({ id: dashboard.id }),
).catch((error: unknown) => {
console.error("Failed dashboard loading metadata", error);
});
await dispatch(loadMetadataForLinkedTargets(dashboard.dashcards));
await dispatch(Dashboards.actions.fetchMetadata({ id: dashboard.id }));
}
if (dashboardType === "transient") {
await dispatch(loadMetadataForDashcards(dashboard.dashcards));
......
......@@ -21,7 +21,7 @@ const loadMetadataForAvailableCards = cards => dispatch => {
return dispatch(loadMetadataForCards(availableCards));
};
export const loadMetadataForLinkedTargets =
const loadMetadataForLinkedTargets =
dashCards => async (dispatch, getState) => {
const linkTargets = dashCards.flatMap(card =>
getLinkTargets(card.visualization_settings),
......
......@@ -25,7 +25,13 @@ import {
} from "metabase/lib/redux";
import * as Urls from "metabase/lib/urls/dashboards";
import { addUndo } from "metabase/redux/undo";
import { DatabaseSchema, FieldSchema, TableSchema } from "metabase/schema";
import {
DashboardSchema,
DatabaseSchema,
FieldSchema,
QuestionSchema,
TableSchema,
} from "metabase/schema";
const COPY_ACTION = `metabase/entities/dashboards/COPY`;
const FETCH_METADATA = "metabase/entities/dashboards/FETCH_METADATA";
......@@ -173,6 +179,8 @@ const Dashboards = createEntity({
databases: [DatabaseSchema],
tables: [TableSchema],
fields: [FieldSchema],
cards: [QuestionSchema],
dashboards: [DashboardSchema],
}),
)(
({ id }) =>
......
......@@ -192,18 +192,23 @@
(cond-> ; card
(card/model? card) (t2/hydrate :persisted)))))
(defn get-card
"Get `Card` with ID."
[id]
(let [raw-card (t2/select-one Card :id id)]
(-> raw-card
api/read-check
hydrate-card-details
;; Cal 2023-11-27: why is last-edit-info hydrated differently for GET vs PUT and POST
(last-edit/with-last-edit-info :card)
collection.root/hydrate-root-collection)))
(api/defendpoint GET "/:id"
"Get `Card` with ID."
[id ignore_view]
{id ms/PositiveInt
ignore_view [:maybe :boolean]}
(let [raw-card (t2/select-one Card :id id)
card (-> raw-card
api/read-check
hydrate-card-details
;; Cal 2023-11-27: why is last-edit-info hydrated differently for GET vs PUT and POST
(last-edit/with-last-edit-info :card)
collection.root/hydrate-root-collection)]
(let [card (get-card id)]
(u/prog1 card
(when-not ignore_view
(events/publish-event! :event/card-read {:object <> :user-id api/*current-user-id*})))))
......
......@@ -7,6 +7,7 @@
[medley.core :as m]
[metabase.actions.core :as actions]
[metabase.analytics.snowplow :as snowplow]
[metabase.api.card :as api.card]
[metabase.api.common :as api]
[metabase.api.common.validation :as validation]
[metabase.api.database :as api.database]
......@@ -845,11 +846,34 @@
(defn- dashboard-metadata
[dashboard]
(let [dashcards (:dashcards dashboard)
cards (for [{:keys [card series]} dashcards
:let [all (conj series card)]
card all
:when (:dataset_query card)]
card)
links (group-by :type (set (for [dashcard dashcards
:let [top-click-behavior (get-in dashcard [:visualization_settings :click_behavior])
col-click-behaviors (keep (comp :click_behavior val)
(get-in dashcard [:visualization_settings :column_settings]))]
{:keys [linkType type targetId]} (conj col-click-behaviors top-click-behavior)
:when (and (= type "link")
(contains? #{"question" "dashboard"} linkType))]
{:type (case linkType
"question" :card
"dashboard" :dashboard)
:id targetId})))
fetch-or-warn (fn [{entity-type :type entity-id :id} f & f-args]
(try
(apply f entity-id f-args)
(catch Exception e
(log/warnf "Error in dashboard metadata %s %s: %s" entity-type entity-id (ex-message e)))))
link-cards (->> (:card links)
(sort-by :id)
(into []
(keep #(fetch-or-warn % api.card/get-card))))
cards (->> (concat
(for [{:keys [card series]} dashcards
:let [all (conj series card)]
card all]
card)
link-cards)
(filter :dataset_query))
database-ids (set (map :database_id cards))
db->mp (into {} (map (juxt identity lib.metadata.jvm/application-database-metadata-provider)
database-ids))
......@@ -859,33 +883,28 @@
query (lib/query mp (:dataset_query card))]
(lib/dependent-metadata query (:id card) (:type card)))) cards)))]
{:tables (->> (:table dependents)
(sort-by (fn [{card-or-table-id :id}]
(or (lib.util/legacy-string-table-id->card-id card-or-table-id) card-or-table-id)))
;; Can be int or "card__<id>"
(sort-by (comp str :id))
(into []
(keep (fn [{card-or-table-id :id}]
(try
(if-let [card-id (lib.util/legacy-string-table-id->card-id card-or-table-id)]
(api.table/fetch-card-query-metadata card-id)
(api.table/fetch-table-query-metadata card-or-table-id {}))
(catch Exception e
(log/warnf "Error in dashboard metadata %s %s: %s" :table card-or-table-id (ex-message e))))))))
(keep #(fetch-or-warn
%
(fn [card-or-table-id]
(if-let [card-id (lib.util/legacy-string-table-id->card-id card-or-table-id)]
(api.table/fetch-card-query-metadata card-id)
(api.table/fetch-table-query-metadata card-or-table-id {})))))))
:databases (->> (:database dependents)
(sort-by :id)
(into []
(keep (fn [{database-id :id}]
(try
(api.database/get-database database-id {})
(catch Exception e
(log/warnf "Error in dashboard metadata %s %s: %s" :database database-id (ex-message e))))))))
(keep #(fetch-or-warn % api.database/get-database {}))))
:fields (->> (:field dependents)
(sort-by :id)
(into []
(keep (fn [{field-id :id}]
(try
(api.field/get-field field-id {})
(catch Exception e
(log/warnf "Error in dashboard metadata %s %s: %s" :field field-id (ex-message e))))))))}))
(keep #(fetch-or-warn % api.field/get-field {}))))
:cards link-cards
:dashboards (->> (:dashboard links)
(sort-by :id)
(into []
(keep #(fetch-or-warn % get-dashboard))))}))
(api/defendpoint GET "/:id/query_metadata"
"Get all of the required query metadata for the cards on dashboard."
......
......@@ -4501,6 +4501,9 @@
(deftest dependent-metadata-test
(mt/with-temp
[Dashboard {dashboard-id :id} {}
Dashboard {link-dash :id} {}
Card {link-card :id} {:dataset_query (mt/mbql-query reviews)
:database_id (mt/id)}
Card {card-id-1 :id} {:dataset_query (mt/mbql-query products)
:database_id (mt/id)}
Card {card-id-2 :id} {:dataset_query
......@@ -4535,9 +4538,17 @@
:query_type :native
:database_id (mt/id)}
DashboardCard {dashcard-id-1 :id} {:dashboard_id dashboard-id,
:card_id card-id-1}
:card_id card-id-1
:visualization_settings {:column_settings
{"[\"name\", 0]" ;; FE reference that must be json formatted
{:click_behavior {:type :link
:linkType "dashboard"
:targetId link-dash}}}}}
DashboardCard _ {:dashboard_id dashboard-id,
:card_id card-id-2}
:card_id card-id-2
:visualization_settings {:click_behavior {:type :link
:linkType "question"
:targetId link-card}}}
Card {series-id-1 :id} {:name "Series Card 1"
:dataset_query (mt/mbql-query checkins)
:database_id (mt/id)}
......@@ -4559,12 +4570,15 @@
:tables (sort-by :id [{:id (mt/id :categories)}
{:id (mt/id :users)}
{:id (mt/id :checkins)}
{:id (mt/id :reviews)}
{:id (mt/id :products)
:fields sequential?
:db map?
:dimension_options map?}
{:id (mt/id :venues)}])
:databases [{:id (mt/id) :engine string?}]}
:cards [{:id link-card}]
:databases [{:id (mt/id) :engine string?}]
:dashboards [{:id link-dash}]}
(-> (mt/user-http-request :crowberto :get 200 (str "dashboard/" dashboard-id "/query_metadata"))
;; The output is so large, these help debugging
#_#_#_
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment