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

Fix question header when the original question is a native query (#48815)

parent 7925599e
No related branches found
No related tags found
No related merge requests found
import type { Card } from "./card";
import type { Card, CardType } from "./card";
import type { Database, DatabaseId, InitialSyncStatus } from "./database";
import type { Field, FieldDimensionOption, FieldId } from "./field";
import type { Segment } from "./segment";
......@@ -22,7 +22,7 @@ export type TableFieldOrder = "database" | "alphabetical" | "custom" | "smart";
export type Table = {
id: TableId;
type?: CardType;
name: string;
display_name: string;
description: string | null;
......
......@@ -18,7 +18,7 @@ export function browseDatabase(database: DatabaseV1 | Database) {
export function browseSchema(table: {
db_id?: Table["db_id"];
schema_name: Table["schema_name"] | null;
schema_name?: Table["schema_name"] | null;
db?: Pick<DatabaseV1, "id">;
}) {
const databaseId = table.db?.id || table.db_id;
......
......@@ -14,7 +14,7 @@ import {
import { HeadBreadcrumbs } from "../HeaderBreadcrumbs";
import { getDataSourceParts } from "./utils";
import { getDataSourceParts, getQuestionIcon } from "./utils";
QuestionDataSource.propTypes = {
question: PropTypes.object,
......@@ -121,7 +121,7 @@ function SourceDatasetBreadcrumbs({ question, ...props }) {
<HeadBreadcrumbs.Badge
key="dataset-collection"
to={Urls.collection(collection)}
icon={question.type() === "metric" ? "metric" : "model"}
icon={getQuestionIcon(question)}
inactiveColor="text-light"
>
{collection?.name || t`Our analytics`}
......
......@@ -3,9 +3,12 @@ import { Component } from "react";
import { createMockMetadata } from "__support__/metadata";
import { setupCardEndpoints } from "__support__/server-mocks/card";
import { renderWithProviders, screen } from "__support__/ui";
import { getIcon, renderWithProviders, screen } from "__support__/ui";
import * as Urls from "metabase/lib/urls";
import * as Lib from "metabase-lib";
import { SAMPLE_METADATA } from "metabase-lib/test-helpers";
import Question from "metabase-lib/v1/Question";
import { getQuestionVirtualTableId } from "metabase-lib/v1/metadata/utils/saved-questions";
import * as ML_Urls from "metabase-lib/v1/urls";
import {
createMockCard,
......@@ -233,12 +236,14 @@ const SOURCE_CARD = createMockCard({ id: SOURCE_QUESTION_ID });
function setup({
card,
originalCard,
subHead = false,
isObjectDetail = false,
hasPermissions = true,
} = {}) {
const metadata = hasPermissions ? getMetadata() : createMockMetadata({});
const question = card && new Question(card, metadata);
const originalQuestion = originalCard && new Question(originalCard, metadata);
setupCardEndpoints(SOURCE_CARD);
......@@ -247,6 +252,7 @@ function setup({
<ErrorBoundary onError={onError}>
<QuestionDataSource
question={question}
originalQuestion={originalQuestion}
subHead={subHead}
isObjectDetail={isObjectDetail}
/>
......@@ -571,4 +577,36 @@ describe("QuestionDataSource", () => {
setup({ card: SOURCE_CARD, subHead: true });
expect(screen.queryByLabelText("More info")).not.toBeInTheDocument();
});
it("should show the correct icon when the original question is a native query", () => {
const metadataProvider = Lib.metadataProvider(
SAMPLE_DB_ID,
SAMPLE_METADATA,
);
const originalQuery = Lib.nativeQuery(
SAMPLE_DB_ID,
metadataProvider,
"SELECT * FROM ORDERS",
);
const originalQuestion = Question.create()
.setId(1)
.setDisplayName("SQL query")
.setQuery(originalQuery);
const newMetadata = createMockMetadata({
databases: [createSampleDatabase()],
questions: [originalQuestion.card()],
});
const newMetadataProvider = Lib.metadataProvider(SAMPLE_DB_ID, newMetadata);
const newQuery = Lib.queryFromTableOrCardMetadata(
newMetadataProvider,
Lib.tableOrCardMetadata(
newMetadataProvider,
getQuestionVirtualTableId(originalQuestion.id()),
),
);
const newQuestion = Question.create().setQuery(newQuery);
setup({ card: newQuestion.card(), originalCard: originalQuestion.card() });
expect(screen.getByText("SQL query")).toBeInTheDocument();
expect(getIcon("table2")).toBeInTheDocument();
});
});
import PropTypes from "prop-types";
import { isValidElement } from "react";
import { type ReactElement, isValidElement } from "react";
import { match } from "ts-pattern";
import { TableInfoIcon } from "metabase/components/MetadataInfo/TableInfoIcon/TableInfoIcon";
import { isNotNull } from "metabase/lib/types";
import * as Urls from "metabase/lib/urls";
import type { IconName } from "metabase/ui";
import * as Lib from "metabase-lib";
import type Question from "metabase-lib/v1/Question";
import type Table from "metabase-lib/v1/metadata/Table";
import {
getQuestionIdFromVirtualTableId,
getQuestionVirtualTableId,
isVirtualCardId,
} from "metabase-lib/v1/metadata/utils/saved-questions";
import type NativeQuery from "metabase-lib/v1/queries/NativeQuery";
import * as ML_Urls from "metabase-lib/v1/urls";
import { HeadBreadcrumbs } from "../HeaderBreadcrumbs";
import { IconWrapper, TablesDivider } from "./QuestionDataSource.styled";
type DataSourcePart = ReactElement | DataSourceBadgePart;
type DataSourceBadgePart = {
name?: string;
href?: string;
icon?: IconName;
model?: "database" | "schema" | "table" | "question" | "model" | "metric";
};
export function getDataSourceParts({
question,
subHead,
isObjectDetail,
formatTableAsComponent = true,
}) {
}: {
question: Question;
subHead?: boolean;
isObjectDetail?: boolean;
formatTableAsComponent?: boolean;
}): DataSourcePart[] {
if (!question) {
return [];
}
......@@ -34,7 +52,7 @@ export function getDataSourceParts({
return [];
}
const parts = [];
const parts: DataSourcePart[] = [];
const metadata = question.metadata();
const database = metadata.database(Lib.databaseID(query));
......@@ -43,21 +61,21 @@ export function getDataSourceParts({
parts.push({
icon: !subHead ? "database" : undefined,
name: database.displayName(),
href: database.id >= 0 && Urls.browseDatabase(database),
href: database.id >= 0 ? Urls.browseDatabase(database) : undefined,
model: "database",
});
}
const table = !isNative
? metadata.table(Lib.sourceTableOrCardId(query))
: question.legacyQuery().table();
: (question.legacyQuery() as NativeQuery).table();
if (table && table.hasSchema()) {
const isBasedOnSavedQuestion = isVirtualCardId(table.id);
if (!isBasedOnSavedQuestion) {
if (database != null && !isBasedOnSavedQuestion) {
parts.push({
model: "schema",
name: table.schema_name,
href: database.id >= 0 && Urls.browseSchema(table),
href: database.id >= 0 ? Urls.browseSchema(table) : undefined,
});
}
}
......@@ -65,10 +83,12 @@ export function getDataSourceParts({
if (table) {
const hasTableLink = subHead || isObjectDetail;
if (isNative) {
return {
name: table.displayName(),
link: hasTableLink ? getTableURL() : "",
};
return [
{
name: table.displayName(),
href: hasTableLink ? getTableURL(table) : "",
},
];
}
const allTables = [
......@@ -88,7 +108,7 @@ export function getDataSourceParts({
}),
].filter(isNotNull);
const part = formatTableAsComponent ? (
const part: DataSourcePart = formatTableAsComponent ? (
<QuestionTableBadges
tables={allTables}
subHead={subHead}
......@@ -106,17 +126,27 @@ export function getDataSourceParts({
parts.push(part);
}
return parts.filter(part => isValidElement(part) || part.name || part.icon);
return parts.filter(
part =>
isValidElement(part) ||
("name" in part && part.name) ||
("icon" in part && part.icon),
);
}
QuestionTableBadges.propTypes = {
tables: PropTypes.arrayOf(PropTypes.object).isRequired,
hasLink: PropTypes.bool,
subHead: PropTypes.bool,
isLast: PropTypes.bool,
type QuestionTableBadgesProps = {
tables: Table[];
subHead?: boolean;
hasLink?: boolean;
isLast?: boolean;
};
function QuestionTableBadges({ tables, subHead, hasLink, isLast }) {
function QuestionTableBadges({
tables,
subHead,
hasLink,
isLast,
}: QuestionTableBadgesProps) {
const badgeInactiveColor = isLast && !subHead ? "text-dark" : "text-light";
const parts = tables.map(table => (
......@@ -151,10 +181,21 @@ function QuestionTableBadges({ tables, subHead, hasLink, isLast }) {
);
}
function getTableURL(table) {
function getTableURL(table: Table) {
if (isVirtualCardId(table.id)) {
const cardId = getQuestionIdFromVirtualTableId(table.id);
return Urls.question({ id: cardId, name: table.displayName() });
if (cardId != null) {
return Urls.question({ id: cardId, name: table.displayName() });
}
}
return ML_Urls.getUrl(table.newQuestion());
}
export function getQuestionIcon(question: Question): IconName {
return match(question.type())
.returnType<IconName>()
.with("question", () => "table2")
.with("model", () => "model")
.with("metric", () => "metric")
.exhaustive();
}
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