Skip to content
Snippets Groups Projects
Unverified Commit 49b461b9 authored by Anton Kulyk's avatar Anton Kulyk Committed by GitHub
Browse files

Handle no saved questions in the data picker (#28099)

* Add `is_saved_questions` to database type

* Support saved questions DB in db endpoint mocks

* Add tests

* Show questions section only when there are questions

* Move `SAVED_QUESTIONS_DB` to `databases/constants`
parent f4151eca
No related branches found
No related tags found
No related merge requests found
......@@ -27,6 +27,7 @@ class DatabaseInner extends Base {
name: string;
engine: string;
description: string;
is_saved_questions: boolean;
tables: Table[];
schemas: Schema[];
metadata: Metadata;
......
......@@ -55,7 +55,7 @@ function mapStateToProps(state: State) {
function DataPicker({
value,
databases,
databases: allDatabases,
search: modelLookupResult,
filters: customFilters = {},
hasNestedQueriesEnabled,
......@@ -74,13 +74,21 @@ function DataPicker({
[customFilters],
);
const databases = useMemo(
() => allDatabases.filter(database => !database.is_saved_questions),
[allDatabases],
);
const dataTypes = useMemo(
() =>
getDataTypes({
hasModels: modelLookupResult.length > 0,
hasSavedQuestions: allDatabases.some(
database => database.is_saved_questions,
),
hasNestedQueriesEnabled,
}).filter(type => filters.types(type.id)),
[filters, modelLookupResult, hasNestedQueriesEnabled],
[allDatabases, filters, modelLookupResult, hasNestedQueriesEnabled],
);
const handleDataTypeChange = useCallback(
......@@ -146,7 +154,9 @@ function DataPicker({
const DataPickerContainer = _.compose(
// Required for `hasDataAccess` check
Databases.loadList(),
Databases.loadList({
query: { saved: true },
}),
// Lets the picker check there is
// at least one model, to offer for selection
......
......@@ -36,7 +36,7 @@ type RawDataPickerProps = RawDataPickerOwnProps & DatabaseListLoaderProps;
function RawDataPicker({
value,
databases,
databases: allDatabases,
isMultiSelect,
allLoading,
onChange,
......@@ -49,6 +49,11 @@ function RawDataPicker({
isMultiSelect,
});
const databases = useMemo(
() => allDatabases.filter(database => !database.is_saved_questions),
[allDatabases],
);
const selectedDatabase = useMemo(() => {
if (!selectedDatabaseId) {
return;
......@@ -193,6 +198,9 @@ function RawDataPicker({
return renderPicker({ isLoading: allLoading });
}
export default Databases.loadList({ loadingAndErrorWrapper: false })(
RawDataPicker,
);
export default Databases.loadList({
loadingAndErrorWrapper: false,
// We don't actually need the saved questions database here,
// but that'd let us reuse DataPickerContainer's DB list loader result
query: { saved: true },
})(RawDataPicker);
......@@ -52,6 +52,11 @@ describe("DataPicker — picking questions", () => {
expect(screen.getByText(/Nothing here/i)).toBeInTheDocument();
});
it("doesn't show section when there are no saved questions", async () => {
await setup({ hasSavedQuestions: false });
expect(screen.queryByText(/Saved Questions/i)).not.toBeInTheDocument();
});
it("respects initial value", async () => {
await setup({
initialValue: {
......
......@@ -50,6 +50,12 @@ describe("DataPicker — picking raw data", () => {
expect(await screen.findByText(/Nothing here/i)).toBeInTheDocument();
});
it("doesn't show saved questions database", async () => {
await setup();
userEvent.click(screen.getByText("Raw Data"));
expect(screen.queryByText(/Saved Questions/i)).not.toBeInTheDocument();
});
it("allows to pick multiple tables", async () => {
const { onChange } = await setup({ isMultiSelect: true });
......
......@@ -149,6 +149,7 @@ interface SetupOpts {
hasDataAccess?: boolean;
hasEmptyDatabase?: boolean;
hasMultiSchemaDatabase?: boolean;
hasSavedQuestions?: boolean;
hasModels?: boolean;
hasNestedQueriesEnabled?: boolean;
}
......@@ -160,6 +161,7 @@ export async function setup({
hasDataAccess = true,
hasEmptyDatabase = false,
hasMultiSchemaDatabase = false,
hasSavedQuestions = true,
hasModels = true,
hasNestedQueriesEnabled = true,
}: SetupOpts = {}) {
......@@ -178,9 +180,9 @@ export async function setup({
databases.push(EMPTY_DATABASE);
}
setupDatabasesEndpoints(scope, databases);
setupDatabasesEndpoints(scope, databases, { hasSavedQuestions });
} else {
setupDatabasesEndpoints(scope, []);
setupDatabasesEndpoints(scope, [], { hasSavedQuestions: false });
}
scope
......
......@@ -11,9 +11,11 @@ export type DataTypeInfoItem = {
export function getDataTypes({
hasNestedQueriesEnabled,
hasSavedQuestions,
hasModels,
}: {
hasNestedQueriesEnabled: boolean;
hasSavedQuestions: boolean;
hasModels: boolean;
}): DataTypeInfoItem[] {
const dataTypes: DataTypeInfoItem[] = [
......@@ -35,12 +37,14 @@ export function getDataTypes({
});
}
dataTypes.push({
id: "questions",
name: t`Saved Questions`,
icon: "folder",
description: t`Use any question’s results to start a new question.`,
});
if (hasSavedQuestions) {
dataTypes.push({
id: "questions",
name: t`Saved Questions`,
icon: "folder",
description: t`Use any question’s results to start a new question.`,
});
}
}
return dataTypes;
......
import React from "react";
import { t } from "ttag";
import { SAVED_QUESTIONS_VIRTUAL_DB_ID } from "metabase-lib/metadata/utils/saved-questions";
import DatabaseAuthCodeDescription from "./components/DatabaseAuthCodeDescription";
import DatabaseCacheScheduleField from "./components/DatabaseCacheScheduleField";
import DatabaseClientIdDescription from "./components/DatabaseClientIdDescription";
......@@ -10,6 +11,13 @@ import DatabaseSslKeyDescription from "./components/DatabaseSslKeyDescription";
import DatabaseSyncScheduleField from "./components/DatabaseSyncScheduleField";
import { EngineFieldOverride } from "./types";
export const SAVED_QUESTIONS_DATABASE = {
id: SAVED_QUESTIONS_VIRTUAL_DB_ID,
name: t`Saved Questions`,
is_saved_questions: true,
features: ["basic-aggregations"],
};
export const ELEVATED_ENGINES = [
"mysql",
"postgres",
......
import { Scope } from "nock";
import _ from "underscore";
import { SAVED_QUESTIONS_DATABASE } from "metabase/databases/constants";
import { Database } from "metabase-types/api";
import { setupTableEndpoints } from "./table";
......@@ -9,8 +10,16 @@ export function setupDatabaseEndpoints(scope: Scope, db: Database) {
db.tables?.forEach(table => setupTableEndpoints(scope, table));
}
export function setupDatabasesEndpoints(scope: Scope, dbs: Database[]) {
export function setupDatabasesEndpoints(
scope: Scope,
dbs: Database[],
{ hasSavedQuestions = true } = {},
) {
scope.get("/api/database").reply(200, dbs);
scope
.get("/api/database?saved=true")
.reply(200, hasSavedQuestions ? [...dbs, SAVED_QUESTIONS_DATABASE] : dbs);
dbs.forEach(db => setupDatabaseEndpoints(scope, db));
}
......
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