diff --git a/e2e/test/scenarios/models/reproductions/35039-37009-missing-result-metadata.cy.spec.js b/e2e/test/scenarios/models/reproductions/35039-37009-missing-result-metadata.cy.spec.js new file mode 100644 index 00000000000000..1ca9acfcd5f241 --- /dev/null +++ b/e2e/test/scenarios/models/reproductions/35039-37009-missing-result-metadata.cy.spec.js @@ -0,0 +1,61 @@ +import { + focusNativeEditor, + modal, + openQuestionActions, + popover, + restore, +} from "e2e/support/helpers"; + +describe("issues 35039 and 37009", () => { + beforeEach(() => { + restore(); + cy.signInAsNormalUser(); + }); + + // This test follows #37009 repro steps because they are simpler than #35039 but still equivalent + it("should show columns available in the model (metabase#35039) (metabase#37009)", () => { + cy.visit("/model/new"); + cy.findByTestId("new-model-options") + .findByText("Use a native query") + .click(); + + focusNativeEditor().type("select * from products -- where true=false"); + + cy.findByTestId("dataset-edit-bar").findByText("Save").click(); + modal() + .last() + .within(() => { + cy.findByLabelText("Name").type("Model").realPress("Tab"); + cy.findByText("Save").click(); + }); + + openQuestionActions(); + popover().findByText("Edit query definition").click(); + + focusNativeEditor().type( + "{backspace}{backspace}{backspace}{backspace}{backspace}", + ); + cy.findByTestId("dataset-edit-bar").within(() => { + cy.findByText("Save changes").click(); + cy.findByText("Saving…").should("not.exist"); + }); + + cy.findByTestId("query-builder-main").within(() => { + cy.findByText("Doing science...").should("be.visible"); + cy.findByText("Doing science...").should("not.exist"); + }); + + cy.icon("notebook").click(); + cy.findByTestId("fields-picker").click(); + popover().within(() => { + cy.findByText("ID").should("exist"); + cy.findByText("EAN").should("exist"); + cy.findByText("TITLE").should("exist"); + cy.findByText("CATEGORY").should("exist"); + cy.findByText("VENDOR").should("exist"); + cy.findByText("PRICE").should("exist"); + cy.findByText("RATING").should("exist"); + cy.findByText("CREATED_AT").should("exist"); + }); + }); +}); diff --git a/frontend/src/metabase/query_builder/actions/core/core.js b/frontend/src/metabase/query_builder/actions/core/core.js index 397191700e3529..aa62043a9daa57 100644 --- a/frontend/src/metabase/query_builder/actions/core/core.js +++ b/frontend/src/metabase/query_builder/actions/core/core.js @@ -30,11 +30,11 @@ import { getCard, getIsResultDirty, getOriginalQuestion, + getParameters, getQuestion, getResultsMetadata, getTransformedSeries, isBasedOnExistingQuestion, - getParameters, } from "../../selectors"; import { updateUrl } from "../navigation"; import { zoomInRow } from "../object-detail"; @@ -248,10 +248,17 @@ export const apiUpdateQuestion = (question, { rerunQuery } = {}) => { const originalQuestion = getOriginalQuestion(getState()); question = question || getQuestion(getState()); - const resultsMetadata = getResultsMetadata(getState()); + let resultsMetadata = getResultsMetadata(getState()); const isResultDirty = getIsResultDirty(getState()); if (question.isDataset()) { + if (!resultsMetadata) { + // Running the question will populate results metadata in redux store. + // Without it getSubmittableQuestion won't have all the necessary information. + await dispatch(runQuestionQuery()); + } + + resultsMetadata = getResultsMetadata(getState()); resultsMetadata.columns = ModelIndexes.actions.cleanIndexFlags( resultsMetadata.columns, ); diff --git a/frontend/src/metabase/query_builder/actions/querying.js b/frontend/src/metabase/query_builder/actions/querying.js index ab0af65a33763c..4df7cebdca81f2 100644 --- a/frontend/src/metabase/query_builder/actions/querying.js +++ b/frontend/src/metabase/query_builder/actions/querying.js @@ -121,7 +121,7 @@ export const runQuestionQuery = ({ const queryTimer = startTimer(); - apiRunQuestionQuery(question, { + const runQuestionPromise = apiRunQuestionQuery(question, { cancelDeferred: cancelQueryDeferred, ignoreCache: ignoreCache, isDirty: cardIsDirty, @@ -140,6 +140,8 @@ export const runQuestionQuery = ({ .catch(error => dispatch(queryErrored(startTime, error))); dispatch({ type: RUN_QUERY, payload: { cancelQueryDeferred } }); + + return runQuestionPromise; }; };