Skip to content

Commit

Permalink
Merge branch 'master' into fix-40196/data-labels-too-dense-with-large…
Browse files Browse the repository at this point in the history
…-single-series
  • Loading branch information
JesseSDevaney committed Jun 1, 2024
2 parents f863c24 + 2c1333e commit 60f4344
Show file tree
Hide file tree
Showing 68 changed files with 997 additions and 479 deletions.
12 changes: 8 additions & 4 deletions docs/developers-guide/api-changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,13 @@ title: API changelog

- `GET /api/dashboard/:id/query_metadata`

New endpoint that combines responses for `/api/field/:id`, `/api/dashboard/:id`, `/api/dashboard/:id/schemas`,
and `/api/table/:id/query_metadata`. This should drastically cut down on the required number of requests to display a
dashboard.
New endpoint that combines responses for `/api/field/:id`, `/api/database/:id`, and `/api/table/:id/query_metadata`.
This should drastically cut down on the required number of requests to display a card.

- `GET /api/card/:id/query_metadata`

New endpoint that combines responses for `/api/field/:id`, `/api/database/:id`, and `/api/table/:id/query_metadata`.
This should drastically cut down on the required number of requests to display a dashboard.

## Metabase 0.50.0

Expand Down Expand Up @@ -63,4 +67,4 @@ NOTE: These endpoint changes were added in 0.49.3, and a bug in `GET /api/embed/

- all endpoints that return data (e.g. exports in JSON, XLSX, CSV, endpoints that end in "/query")

Starting from v49, we respond to the API calls with values formatted according to the instance localization options
Starting from v49, we respond to the API calls with values formatted according to the instance localization options
4 changes: 2 additions & 2 deletions e2e/support/helpers/e2e-notebook-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,15 +173,15 @@ export function selectSavedQuestionsToJoin(
firstQuestionName: string,
secondQuestionName: string,
) {
cy.intercept("GET", "/api/database/*/schemas").as("loadSchemas");
cy.intercept("GET", "/api/table/*/query_metadata").as("joinedTableMetadata");
entityPickerModal().within(() => {
entityPickerModalTab("Models").should("exist");
entityPickerModalTab("Tables").should("exist");
entityPickerModalTab("Saved questions").click();
cy.findByText(firstQuestionName).click();
});

cy.wait("@loadSchemas");
cy.wait("@joinedTableMetadata");

// join to question b
cy.icon("join_left_outer").click();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ describeEE("scenarios > Metabase Analytics Collection (AuditV2) ", () => {
expect(response.statusCode).to.eq(200);
});

modal().button("Not now").click();
cy.button("Not now").click();

cy.log("saving copied question");

Expand Down
205 changes: 185 additions & 20 deletions e2e/test/scenarios/dashboard/tabs.cy.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ import {
modal,
addHeadingWhileEditing,
setFilter,
openStaticEmbeddingModal,
publishChanges,
visitIframe,
} from "e2e/support/helpers";
import { createMockDashboardCard } from "metabase-types/api/mocks";

Expand Down Expand Up @@ -431,34 +434,72 @@ describe("scenarios > dashboard > tabs", () => {
cy.intercept(
"POST",
`/api/dashboard/${ORDERS_DASHBOARD_ID}/dashcard/${ORDERS_DASHBOARD_DASHCARD_ID}/card/${ORDERS_QUESTION_ID}/query`,
req => {
req.on("response", cy.spy().as("firstTabQuery"));
},
);
cy.spy().as("firstTabQuerySpy"),
).as("firstTabQuery");

cy.get("@secondTabDashcardId").then(secondTabDashcardId => {
cy.intercept(
"POST",
`/api/dashboard/${ORDERS_DASHBOARD_ID}/dashcard/${secondTabDashcardId}/card/${ORDERS_COUNT_QUESTION_ID}/query`,
cy.spy().as("secondTabQuery"),
);
cy.spy().as("secondTabQuerySpy"),
).as("secondTabQuery");
});

const firstQuestion = () => {
return cy.request("GET", `/api/card/${ORDERS_QUESTION_ID}`).its("body");
};
const secondQuestion = () => {
return cy
.request("GET", `/api/card/${ORDERS_COUNT_QUESTION_ID}`)
.its("body");
};

firstQuestion().then(r => {
expect(r.view_count).to.equal(1);
});
secondQuestion().then(r => {
expect(r.view_count).to.equal(1);
});

// Visit first tab and confirm only first card was queried
visitDashboard(ORDERS_DASHBOARD_ID);

cy.get("@firstTabQuery").should("have.been.calledOnce");
cy.get("@secondTabQuery").should("not.have.been.called");
cy.get("@firstTabQuerySpy").should("have.been.calledOnce");
cy.get("@secondTabQuerySpy").should("not.have.been.called");
cy.wait("@firstTabQuery").then(r => {
firstQuestion().then(r => {
expect(r.view_count).to.equal(3); // 1 (previously) + 1 (firstQuestion) + 1 (firstTabQuery)
});
secondQuestion().then(r => {
expect(r.view_count).to.equal(2); // 1 (previously) + 1 (secondQuestion)
});
});

// Visit second tab and confirm only second card was queried
goToTab("Tab 2");
cy.get("@firstTabQuery").should("have.been.calledOnce");
cy.get("@secondTabQuery").should("have.been.calledOnce");
cy.get("@secondTabQuerySpy").should("have.been.calledOnce");
cy.get("@firstTabQuerySpy").should("have.been.calledOnce");
cy.wait("@secondTabQuery").then(r => {
firstQuestion().then(r => {
expect(r.view_count).to.equal(4); // 3 (previously) + 1 (firstQuestion)
});
secondQuestion().then(r => {
expect(r.view_count).to.equal(4); // 2 (previously) + 1 (secondQuestion) + 1 (secondTabQuery)
});
});

// Go back to first tab, expect no additional queries
goToTab("Tab 1");
cy.get("@firstTabQuery").should("have.been.calledOnce");
cy.get("@secondTabQuery").should("have.been.calledOnce");
cy.findAllByTestId("dashcard").contains("37.65");
cy.get("@firstTabQuerySpy").should("have.been.calledOnce");
cy.get("@secondTabQuerySpy").should("have.been.calledOnce");

firstQuestion().then(r => {
expect(r.view_count).to.equal(5); // 4 (previously) + 1 (firstQuestion)
});
secondQuestion().then(r => {
expect(r.view_count).to.equal(5); // 4 (previously) + 1 (secondQuestion)
});

// Go to public dashboard
cy.request("PUT", "/api/setting/enable-public-sharing", { value: true });
Expand All @@ -469,27 +510,151 @@ describe("scenarios > dashboard > tabs", () => {
cy.intercept(
"GET",
`/api/public/dashboard/${uuid}/dashcard/${ORDERS_DASHBOARD_DASHCARD_ID}/card/${ORDERS_QUESTION_ID}?parameters=%5B%5D`,
cy.spy().as("publicFirstTabQuery"),
);
cy.spy().as("publicFirstTabQuerySpy"),
).as("publicFirstTabQuery");
cy.get("@secondTabDashcardId").then(secondTabDashcardId => {
cy.intercept(
"GET",
`/api/public/dashboard/${uuid}/dashcard/${secondTabDashcardId}/card/${ORDERS_COUNT_QUESTION_ID}?parameters=%5B%5D`,
cy.spy().as("publicSecondTabQuery"),
);
cy.spy().as("publicSecondTabQuerySpy"),
).as("publicSecondTabQuery");
});

cy.visit(`public/dashboard/${uuid}`);
});

// Check first tab requests
cy.get("@publicFirstTabQuery").should("have.been.calledOnce");
cy.get("@publicSecondTabQuery").should("not.have.been.called");
cy.get("@publicFirstTabQuerySpy").should("have.been.calledOnce");
cy.get("@publicSecondTabQuerySpy").should("not.have.been.called");
cy.wait("@publicFirstTabQuery").then(r => {
firstQuestion().then(r => {
expect(r.view_count).to.equal(7); // 5 (previously) + 1 (firstQuestion) + 1 (publicFirstTabQuery)
});
secondQuestion().then(r => {
expect(r.view_count).to.equal(6); // 5 (previously) + 1 (secondQuestion)
});
});

// Visit second tab and confirm only second card was queried
goToTab("Tab 2");
cy.get("@publicFirstTabQuery").should("have.been.calledOnce");
cy.get("@publicSecondTabQuery").should("have.been.calledOnce");
cy.get("@publicSecondTabQuerySpy").should("have.been.calledOnce");
cy.get("@publicFirstTabQuerySpy").should("have.been.calledOnce");
cy.wait("@publicSecondTabQuery").then(r => {
firstQuestion().then(r => {
expect(r.view_count).to.equal(8); // 7 (previously) + 1 (firstQuestion)
});
secondQuestion().then(r => {
expect(r.view_count).to.equal(8); // 6 (previously) + 1 (secondQuestion) + 1 (publicSecondTabQuery)
});
});

goToTab("Tab 1");
// This is a bug. publicFirstTabQuery should not be called again
cy.get("@publicFirstTabQuerySpy").should("have.been.calledTwice");
cy.get("@publicSecondTabQuerySpy").should("have.been.calledOnce");
cy.wait("@publicFirstTabQuery").then(r => {
firstQuestion().then(r => {
expect(r.view_count).to.equal(10); // 8 (previously) + 1 (firstQuestion) + 1 (publicFirstTabQuery)
});
secondQuestion().then(r => {
expect(r.view_count).to.equal(9); // 8 (previously) + 1 (secondQuestion)
});
});
});

it("should only fetch cards on the current tab of an embedded dashboard", () => {
cy.intercept("PUT", "/api/dashboard/*").as("saveDashboardCards");
cy.intercept("POST", "/api/card/*/query").as("cardQuery");

visitDashboardAndCreateTab({
dashboardId: ORDERS_DASHBOARD_ID,
save: false,
});

// Add card to second tab
cy.icon("pencil").click();
openQuestionsSidebar();
sidebar().within(() => {
cy.findByText("Orders, Count").click();
});
cy.wait("@cardQuery");
saveDashboard();
cy.wait("@saveDashboardCards").then(({ response }) => {
cy.wrap(response.body.dashcards[1].id).as("secondTabDashcardId");
});

const firstQuestion = () => {
return cy.request("GET", `/api/card/${ORDERS_QUESTION_ID}`).its("body");
};
const secondQuestion = () => {
return cy
.request("GET", `/api/card/${ORDERS_COUNT_QUESTION_ID}`)
.its("body");
};

firstQuestion().then(r => {
expect(r.view_count).to.equal(1); // 1 (firstQuestion)
});
secondQuestion().then(r => {
expect(r.view_count).to.equal(1); // 1 (secondQuestion)
});

cy.intercept(
"GET",
`/api/embed/dashboard/*/dashcard/*/card/${ORDERS_QUESTION_ID}`,
cy.spy().as("firstTabQuerySpy"),
).as("firstTabQuery");
cy.intercept(
"GET",
`/api/embed/dashboard/*/dashcard/*/card/${ORDERS_COUNT_QUESTION_ID}`,
cy.spy().as("secondTabQuerySpy"),
).as("secondTabQuery");

openStaticEmbeddingModal({ activeTab: "parameters", acceptTerms: true });

// publish the embedded dashboard so that we can directly navigate to its url
publishChanges("dashboard", () => {});
// directly navigate to the embedded dashboard, starting on Tab 1
visitIframe();
// wait for results
cy.findAllByTestId("dashcard").contains("37.65");
cy.signInAsAdmin();
cy.get("@firstTabQuerySpy").should("have.been.calledOnce");
cy.get("@secondTabQuerySpy").should("not.have.been.called");

cy.wait("@firstTabQuery").then(r => {
firstQuestion().then(r => {
expect(r.view_count).to.equal(3); // 1 (previously) + 1 (firstQuestion) + 1 (first tab query)
});
secondQuestion().then(r => {
expect(r.view_count).to.equal(2); // 1 (previously) + 1 (secondQuestion)
});
});

goToTab("Tab 2");
cy.get("@secondTabQuerySpy").should("have.been.calledOnce");
cy.get("@firstTabQuerySpy").should("have.been.calledOnce");
cy.wait("@secondTabQuery").then(r => {
firstQuestion().then(r => {
expect(r.view_count).to.equal(4); // 3 (previously) + 1 (firstQuestion)
});
secondQuestion().then(r => {
expect(r.view_count).to.equal(4); // 2 (previously) + 1 (secondQuestion) + 1 (second tab query)
});
});

goToTab("Tab 1");
// This is a bug. firstTabQuery should not be called again
cy.get("@firstTabQuerySpy").should("have.been.calledTwice");
cy.get("@secondTabQuerySpy").should("have.been.calledOnce");
cy.wait("@firstTabQuery").then(r => {
firstQuestion().then(r => {
expect(r.view_count).to.equal(6); // 4 (previously) + 1 (firstQuestion) + 1 (first tab query)
});
secondQuestion().then(r => {
expect(r.view_count).to.equal(5); // 4 (previously) + 1 (secondQuestion)
});
});
});

it("should apply filter and show loading spinner when changing tabs (#33767)", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
openOrdersTable,
visitDashboard,
queryBuilderHeader,
modal,
chartPathWithFillColor,
} from "e2e/support/helpers";

Expand Down Expand Up @@ -33,7 +32,7 @@ describe("issue 23293", () => {
});

cy.wait("@saveQuestion").then(({ response }) => {
modal().button("Not now").click();
cy.button("Not now").click();

const id = response.body.id;
const questionDetails = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,7 @@ describe("issue 30165", () => {
cy.findByText("Save").click();
});
cy.wait("@createQuestion");
modal().button("Not now").click();
cy.button("Not now").click();

cy.findByTestId("native-query-editor").type(" WHERE TOTAL < 20");
queryBuilderHeader().findByText("Save").click();
Expand Down
10 changes: 3 additions & 7 deletions e2e/test/scenarios/question/saved.cy.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ describe("scenarios > question > saved", () => {
cy.findByText("Save").click();
});
cy.wait("@cardCreate");
modal().button("Not now").click();
cy.button("Not now").click();

// Add a filter in order to be able to save question again
cy.findAllByTestId("action-buttons").last().findByText("Filter").click();
Expand Down Expand Up @@ -130,9 +130,7 @@ describe("scenarios > question > saved", () => {
cy.wait("@cardCreate");
});

modal().within(() => {
cy.findByText("Not now").click();
});
cy.button("Not now").click();

cy.findByTestId("qb-header-left-side").within(() => {
cy.findByDisplayValue("Orders - Duplicate");
Expand Down Expand Up @@ -174,9 +172,7 @@ describe("scenarios > question > saved", () => {
cy.wait("@cardCreate");
});

modal().within(() => {
cy.findByText("Not now").click();
});
cy.button("Not now").click();

cy.findByTestId("qb-header-left-side").within(() => {
cy.findByDisplayValue("Orders - Duplicate");
Expand Down
Loading

0 comments on commit 60f4344

Please sign in to comment.