feat(rightmenu): Add Datasets to + Menu and Hide Databases when one has been connected (#21530)

This commit is contained in:
Antonio Rivero Martinez 2022-10-24 13:45:23 -03:00 committed by GitHub
parent 175ec854b9
commit c19708b432
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 374 additions and 207 deletions

View File

@ -17,7 +17,6 @@
* under the License.
*/
import React, { FunctionComponent, useState, useEffect } from 'react';
import { useHistory } from 'react-router-dom';
import { styled, t } from '@superset-ui/core';
import { useSingleViewResource } from 'src/views/CRUD/hooks';
import Modal from 'src/components/Modal';
@ -29,6 +28,7 @@ import {
LocalStorageKeys,
setItem,
} from 'src/utils/localStorageHelpers';
import { isEmpty } from 'lodash';
type DatasetAddObject = {
id: number;
@ -42,6 +42,7 @@ interface DatasetModalProps {
onDatasetAdd?: (dataset: DatasetAddObject) => void;
onHide: () => void;
show: boolean;
history?: any; // So we can render the modal when not using SPA
}
const TableSelectorContainer = styled.div`
@ -54,8 +55,8 @@ const DatasetModal: FunctionComponent<DatasetModalProps> = ({
onDatasetAdd,
onHide,
show,
history,
}) => {
const history = useHistory();
const [currentDatabase, setCurrentDatabase] = useState<
DatabaseObject | undefined
>();
@ -128,8 +129,16 @@ const DatasetModal: FunctionComponent<DatasetModalProps> = ({
if (onDatasetAdd) {
onDatasetAdd({ id: response.id, ...response });
}
history.push(`/chart/add?dataset=${currentTableName}`);
cleanup();
// We need to be able to work with no SPA routes opening the modal
// So useHistory wont be available always thus we check for it
if (!isEmpty(history)) {
history?.push(`/chart/add?dataset=${currentTableName}`);
cleanup();
} else {
window.location.href = `/chart/add?dataset=${currentTableName}`;
cleanup();
onHide();
}
});
};

View File

@ -725,6 +725,7 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
show={datasetAddModalOpen}
onHide={closeDatasetAddModal}
onDatasetAdd={refreshData}
history={history}
/>
{datasetCurrentlyDeleting && (
<DeleteModal

View File

@ -19,22 +19,90 @@
import React from 'react';
import * as reactRedux from 'react-redux';
import fetchMock from 'fetch-mock';
import waitForComponentToPaint from 'spec/helpers/waitForComponentToPaint';
import { styledMount as mount } from 'spec/helpers/theming';
import { render, screen, waitFor } from 'spec/helpers/testing-library';
import userEvent from '@testing-library/user-event';
import RightMenu from './RightMenu';
import { RightMenuProps } from './types';
import { GlobalMenuDataOptions, RightMenuProps } from './types';
jest.mock('react-redux', () => ({
...jest.requireActual('react-redux'),
useSelector: jest.fn(),
}));
jest.mock('src/views/CRUD/data/database/DatabaseModal', () => () => <span />);
jest.mock('src/views/CRUD/data/dataset/AddDatasetModal.tsx', () => () => (
<span />
));
const dropdownItems = [
{
label: 'Data',
icon: 'fa-database',
childs: [
{
label: 'Connect database',
name: GlobalMenuDataOptions.DB_CONNECTION,
perm: true,
},
{
label: 'Create dataset',
name: GlobalMenuDataOptions.DATASET_CREATION,
perm: true,
},
{
label: 'Connect Google Sheet',
name: GlobalMenuDataOptions.GOOGLE_SHEETS,
perm: true,
},
{
label: 'Upload CSV to database',
name: 'Upload a CSV',
url: '/csvtodatabaseview/form',
perm: true,
},
{
label: 'Upload columnar file to database',
name: 'Upload a Columnar file',
url: '/columnartodatabaseview/form',
perm: true,
},
{
label: 'Upload Excel file to database',
name: 'Upload Excel',
url: '/exceltodatabaseview/form',
perm: true,
},
],
},
{
label: 'SQL query',
url: '/superset/sqllab?new=true',
icon: 'fa-fw fa-search',
perm: 'can_sqllab',
view: 'Superset',
},
{
label: 'Chart',
url: '/chart/add',
icon: 'fa-fw fa-bar-chart',
perm: 'can_write',
view: 'Chart',
},
{
label: 'Dashboard',
url: '/dashboard/new',
icon: 'fa-fw fa-dashboard',
perm: 'can_write',
view: 'Dashboard',
},
];
const createProps = (): RightMenuProps => ({
align: 'flex-end',
navbarRight: {
show_watermark: false,
bug_report_url: '/report/',
documentation_url: '/docs/',
bug_report_url: undefined,
documentation_url: undefined,
languages: {
en: {
flag: 'us',
@ -47,8 +115,8 @@ const createProps = (): RightMenuProps => ({
url: '/lang/it',
},
},
show_language_picker: true,
user_is_anonymous: true,
show_language_picker: false,
user_is_anonymous: false,
user_info_url: '/users/userinfo/',
user_logout_url: '/logout/',
user_login_url: '/login/',
@ -58,38 +126,15 @@ const createProps = (): RightMenuProps => ({
version_sha: 'randomSHA',
build_number: 'randomBuildNumber',
},
settings: [
{
name: 'Security',
icon: 'fa-cogs',
label: 'Security',
index: 1,
childs: [
{
name: 'List Users',
icon: 'fa-user',
label: 'List Users',
url: '/users/list/',
index: 1,
},
],
},
],
settings: [],
isFrontendRoute: () => true,
environmentTag: {
color: 'error.base',
text: 'Development',
text: 'Development2',
},
});
const useSelectorMock = jest.spyOn(reactRedux, 'useSelector');
const useStateMock = jest.spyOn(React, 'useState');
let setShowModal: any;
let setEngine: any;
let setAllowUploads: any;
const mockNonGSheetsDBs = [...new Array(2)].map((_, i) => ({
const mockNonExamplesDB = [...new Array(2)].map((_, i) => ({
changed_by: {
first_name: `user`,
last_name: `${i}`,
@ -108,161 +153,205 @@ const mockNonGSheetsDBs = [...new Array(2)].map((_, i) => ({
},
}));
const mockGsheetsDbs = [...new Array(2)].map((_, i) => ({
changed_by: {
first_name: `user`,
last_name: `${i}`,
},
database_name: `db ${i}`,
backend: 'gsheets',
allow_run_async: true,
allow_dml: false,
allow_file_upload: true,
expose_in_sqllab: false,
changed_on_delta_humanized: `${i} day(s) ago`,
changed_on: new Date().toISOString,
id: i,
engine_information: {
supports_file_upload: false,
},
}));
const useSelectorMock = jest.spyOn(reactRedux, 'useSelector');
describe('RightMenu', () => {
const mockedProps = createProps();
beforeEach(async () => {
useSelectorMock.mockReset();
useStateMock.mockReset();
fetchMock.get(
'glob:*api/v1/database/?q=(filters:!((col:allow_file_upload,opr:upload_is_enabled,value:!t)))',
{ result: [], database_count: 0 },
);
// By default we get file extensions to be uploaded
useSelectorMock.mockReturnValue({
CSV_EXTENSIONS: ['csv'],
EXCEL_EXTENSIONS: ['xls', 'xlsx'],
COLUMNAR_EXTENSIONS: ['parquet', 'zip'],
ALLOWED_EXTENSIONS: ['parquet', 'zip', 'xls', 'xlsx', 'csv'],
});
setShowModal = jest.fn();
setEngine = jest.fn();
setAllowUploads = jest.fn();
const mockSetStateModal: any = (x: any) => [x, setShowModal];
const mockSetStateEngine: any = (x: any) => [x, setEngine];
const mockSetStateAllow: any = (x: any) => [x, setAllowUploads];
useStateMock.mockImplementationOnce(mockSetStateModal);
useStateMock.mockImplementationOnce(mockSetStateEngine);
useStateMock.mockImplementationOnce(mockSetStateAllow);
});
afterEach(fetchMock.restore);
it('renders', async () => {
const wrapper = mount(<RightMenu {...mockedProps} />);
await waitForComponentToPaint(wrapper);
expect(wrapper.find(RightMenu)).toExist();
});
it('If user has permission to upload files we query the existing DBs that has allow_file_upload as True', async () => {
useSelectorMock.mockReturnValueOnce({
createdOn: '2021-04-27T18:12:38.952304',
email: 'admin',
firstName: 'admin',
isActive: true,
lastName: 'admin',
permissions: {},
roles: {
Admin: [
['can_this_form_get', 'CsvToDatabaseView'], // So we can upload CSV
],
},
userId: 1,
username: 'admin',
});
// Second call we get the dashboardId
useSelectorMock.mockReturnValueOnce('1');
const wrapper = mount(<RightMenu {...mockedProps} />);
await waitForComponentToPaint(wrapper);
const callsD = fetchMock.calls(/database\/\?q/);
expect(callsD).toHaveLength(1);
expect(callsD[0][0]).toMatchInlineSnapshot(
`"http://localhost/api/v1/database/?q=(filters:!((col:allow_file_upload,opr:upload_is_enabled,value:!t)))"`,
);
});
it('If user has no permission to upload files the query API should not be called', async () => {
useSelectorMock.mockReturnValueOnce({
createdOn: '2021-04-27T18:12:38.952304',
email: 'admin',
firstName: 'admin',
isActive: true,
lastName: 'admin',
permissions: {},
roles: {
Admin: [['can_write', 'Chart']], // no file permissions
},
userId: 1,
username: 'admin',
});
// Second call we get the dashboardId
useSelectorMock.mockReturnValueOnce('1');
const wrapper = mount(<RightMenu {...mockedProps} />);
await waitForComponentToPaint(wrapper);
const callsD = fetchMock.calls(/database\/\?q/);
expect(callsD).toHaveLength(0);
});
it('If user has permission to upload files but there are only gsheets and clickhouse DBs', async () => {
fetchMock.get(
'glob:*api/v1/database/?q=(filters:!((col:allow_file_upload,opr:upload_is_enabled,value:!t)))',
{ result: [...mockGsheetsDbs], database_count: 2 },
{ overwriteRoutes: true },
);
useSelectorMock.mockReturnValueOnce({
createdOn: '2021-04-27T18:12:38.952304',
email: 'admin',
firstName: 'admin',
isActive: true,
lastName: 'admin',
permissions: {},
roles: {
Admin: [
['can_this_form_get', 'CsvToDatabaseView'], // So we can upload CSV
],
},
userId: 1,
username: 'admin',
});
// Second call we get the dashboardId
useSelectorMock.mockReturnValueOnce('1');
const wrapper = mount(<RightMenu {...mockedProps} />);
await waitForComponentToPaint(wrapper);
const callsD = fetchMock.calls(/database\/\?q/);
expect(callsD).toHaveLength(1);
expect(setAllowUploads).toHaveBeenCalledWith(false);
});
it('If user has permission to upload files and some DBs with allow_file_upload are not gsheets nor clickhouse', async () => {
fetchMock.get(
'glob:*api/v1/database/?q=(filters:!((col:allow_file_upload,opr:upload_is_enabled,value:!t)))',
{ result: [...mockNonGSheetsDBs, ...mockGsheetsDbs], database_count: 2 },
{ overwriteRoutes: true },
);
useSelectorMock.mockReturnValueOnce({
createdOn: '2021-04-27T18:12:38.952304',
email: 'admin',
firstName: 'admin',
isActive: true,
lastName: 'admin',
permissions: {},
roles: {
Admin: [
['can_this_form_get', 'CsvToDatabaseView'], // So we can upload CSV
],
},
userId: 1,
username: 'admin',
});
// Second call we get the dashboardId
useSelectorMock.mockReturnValueOnce('1');
const wrapper = mount(<RightMenu {...mockedProps} />);
await waitForComponentToPaint(wrapper);
const callsD = fetchMock.calls(/database\/\?q/);
expect(callsD).toHaveLength(1);
expect(setAllowUploads).toHaveBeenCalledWith(true);
});
beforeEach(async () => {
useSelectorMock.mockReset();
fetchMock.get(
'glob:*api/v1/database/?q=(filters:!((col:allow_file_upload,opr:upload_is_enabled,value:!t)))',
{ result: [], count: 0 },
);
fetchMock.get(
'glob:*api/v1/database/?q=(filters:!((col:database_name,opr:neq,value:examples)))',
{ result: [], count: 0 },
);
});
afterEach(fetchMock.restore);
const resetUseSelectorMock = () => {
useSelectorMock.mockReturnValueOnce({
createdOn: '2021-04-27T18:12:38.952304',
email: 'admin',
firstName: 'admin',
isActive: true,
lastName: 'admin',
permissions: {},
roles: {
Admin: [
['can_this_form_get', 'CsvToDatabaseView'], // So we can upload CSV
['can_write', 'Database'], // So we can write DBs
['can_write', 'Dataset'], // So we can write Datasets
['can_write', 'Chart'], // So we can write Datasets
],
},
userId: 1,
username: 'admin',
});
// By default we get file extensions to be uploaded
useSelectorMock.mockReturnValueOnce('1');
// By default we get file extensions to be uploaded
useSelectorMock.mockReturnValueOnce({
CSV_EXTENSIONS: ['csv'],
EXCEL_EXTENSIONS: ['xls', 'xlsx'],
COLUMNAR_EXTENSIONS: ['parquet', 'zip'],
ALLOWED_EXTENSIONS: ['parquet', 'zip', 'xls', 'xlsx', 'csv'],
});
};
test('renders', async () => {
const mockedProps = createProps();
// Initial Load
resetUseSelectorMock();
const { container } = render(<RightMenu {...mockedProps} />, {
useRedux: true,
useQueryParams: true,
});
// expect(await screen.findByText(/Settings/i)).toBeInTheDocument();
await waitFor(() => expect(container).toBeInTheDocument());
});
test('If user has permission to upload files AND connect DBs we query existing DBs that has allow_file_upload as True and DBs that are not examples', async () => {
const mockedProps = createProps();
// Initial Load
resetUseSelectorMock();
const { container } = render(<RightMenu {...mockedProps} />, {
useRedux: true,
useQueryParams: true,
});
await waitFor(() => expect(container).toBeVisible());
const callsD = fetchMock.calls(/database\/\?q/);
expect(callsD).toHaveLength(2);
expect(callsD[0][0]).toMatchInlineSnapshot(
`"http://localhost/api/v1/database/?q=(filters:!((col:allow_file_upload,opr:upload_is_enabled,value:!t)))"`,
);
expect(callsD[1][0]).toMatchInlineSnapshot(
`"http://localhost/api/v1/database/?q=(filters:!((col:database_name,opr:neq,value:examples)))"`,
);
});
test('If only examples DB exist we must show the Connect Database option', async () => {
const mockedProps = createProps();
fetchMock.get(
'glob:*api/v1/database/?q=(filters:!((col:allow_file_upload,opr:upload_is_enabled,value:!t)))',
{ result: [...mockNonExamplesDB], count: 2 },
{ overwriteRoutes: true },
);
fetchMock.get(
'glob:*api/v1/database/?q=(filters:!((col:database_name,opr:neq,value:examples)))',
{ result: [], count: 0 },
{ overwriteRoutes: true },
);
// Initial Load
resetUseSelectorMock();
// setAllowUploads called
resetUseSelectorMock();
render(<RightMenu {...mockedProps} />, {
useRedux: true,
useQueryParams: true,
useRouter: true,
});
const dropdown = screen.getByTestId('new-dropdown-icon');
userEvent.hover(dropdown);
const dataMenu = await screen.findByText(dropdownItems[0].label);
userEvent.hover(dataMenu);
expect(await screen.findByText('Connect database')).toBeInTheDocument();
expect(screen.queryByText('Create dataset')).not.toBeInTheDocument();
});
test('If more than just examples DB exist we must show the Create dataset option', async () => {
const mockedProps = createProps();
fetchMock.get(
'glob:*api/v1/database/?q=(filters:!((col:allow_file_upload,opr:upload_is_enabled,value:!t)))',
{ result: [...mockNonExamplesDB], count: 2 },
{ overwriteRoutes: true },
);
fetchMock.get(
'glob:*api/v1/database/?q=(filters:!((col:database_name,opr:neq,value:examples)))',
{ result: [...mockNonExamplesDB], count: 2 },
{ overwriteRoutes: true },
);
// Initial Load
resetUseSelectorMock();
// setAllowUploads called
resetUseSelectorMock();
// setNonExamplesDBConnected called
resetUseSelectorMock();
render(<RightMenu {...mockedProps} />, {
useRedux: true,
useQueryParams: true,
useRouter: true,
});
const dropdown = screen.getByTestId('new-dropdown-icon');
userEvent.hover(dropdown);
const dataMenu = await screen.findByText(dropdownItems[0].label);
userEvent.hover(dataMenu);
expect(await screen.findByText('Create dataset')).toBeInTheDocument();
expect(screen.queryByText('Connect database')).not.toBeInTheDocument();
});
test('If there is a DB with allow_file_upload set as True the option should be enabled', async () => {
const mockedProps = createProps();
fetchMock.get(
'glob:*api/v1/database/?q=(filters:!((col:allow_file_upload,opr:upload_is_enabled,value:!t)))',
{ result: [...mockNonExamplesDB], count: 2 },
{ overwriteRoutes: true },
);
fetchMock.get(
'glob:*api/v1/database/?q=(filters:!((col:database_name,opr:neq,value:examples)))',
{ result: [...mockNonExamplesDB], count: 2 },
{ overwriteRoutes: true },
);
// Initial load
resetUseSelectorMock();
// setAllowUploads called
resetUseSelectorMock();
// setNonExamplesDBConnected called
resetUseSelectorMock();
render(<RightMenu {...mockedProps} />, {
useRedux: true,
useQueryParams: true,
useRouter: true,
});
const dropdown = screen.getByTestId('new-dropdown-icon');
userEvent.hover(dropdown);
const dataMenu = await screen.findByText(dropdownItems[0].label);
userEvent.hover(dataMenu);
expect(
(await screen.findByText('Upload CSV to database')).closest('a'),
).toHaveAttribute('href', '/csvtodatabaseview/form');
});
test('If there is NOT a DB with allow_file_upload set as True the option should be disabled', async () => {
const mockedProps = createProps();
fetchMock.get(
'glob:*api/v1/database/?q=(filters:!((col:allow_file_upload,opr:upload_is_enabled,value:!t)))',
{ result: [], count: 0 },
{ overwriteRoutes: true },
);
fetchMock.get(
'glob:*api/v1/database/?q=(filters:!((col:database_name,opr:neq,value:examples)))',
{ result: [...mockNonExamplesDB], count: 2 },
{ overwriteRoutes: true },
);
// Initial load
resetUseSelectorMock();
// setAllowUploads called
resetUseSelectorMock();
// setNonExamplesDBConnected called
resetUseSelectorMock();
render(<RightMenu {...mockedProps} />, {
useRedux: true,
useQueryParams: true,
useRouter: true,
});
const dropdown = screen.getByTestId('new-dropdown-icon');
userEvent.hover(dropdown);
const dataMenu = await screen.findByText(dropdownItems[0].label);
userEvent.hover(dataMenu);
expect(await screen.findByText('Upload CSV to database')).toBeInTheDocument();
expect(
(await screen.findByText('Upload CSV to database')).closest('a'),
).not.toBeInTheDocument();
});

View File

@ -16,11 +16,12 @@
* specific language governing permissions and limitations
* under the License.
*/
import React, { Fragment, useEffect } from 'react';
import React, { Fragment, useState, useEffect } from 'react';
import rison from 'rison';
import { useSelector } from 'react-redux';
import { Link } from 'react-router-dom';
import { useQueryParams, BooleanParam } from 'use-query-params';
import { isEmpty } from 'lodash';
import {
t,
@ -48,6 +49,7 @@ import {
RightMenuProps,
} from './types';
import { MenuObjectProps } from './Menu';
import AddDatasetModal from '../CRUD/data/dataset/AddDatasetModal';
const extensionsRegistry = getExtensionsRegistry();
@ -91,6 +93,13 @@ const tagStyles = (theme: SupersetTheme) => css`
color: ${theme.colors.grayscale.light5};
`;
const styledChildMenu = (theme: SupersetTheme) => css`
&:hover {
color: ${theme.colors.primary.base} !important;
cursor: pointer !important;
}
`;
const { SubMenu } = Menu;
const RightMenu = ({
@ -101,7 +110,13 @@ const RightMenu = ({
environmentTag,
setQuery,
}: RightMenuProps & {
setQuery: ({ databaseAdded }: { databaseAdded: boolean }) => void;
setQuery: ({
databaseAdded,
datasetAdded,
}: {
databaseAdded?: boolean;
datasetAdded?: boolean;
}) => void;
}) => {
const user = useSelector<any, UserWithPermissionsAndRoles>(
state => state.user,
@ -118,12 +133,14 @@ const RightMenu = ({
ALLOWED_EXTENSIONS,
HAS_GSHEETS_INSTALLED,
} = useSelector<any, ExtentionConfigs>(state => state.common.conf);
const [showModal, setShowModal] = React.useState<boolean>(false);
const [engine, setEngine] = React.useState<string>('');
const [showDatabaseModal, setShowDatabaseModal] = useState<boolean>(false);
const [showDatasetModal, setShowDatasetModal] = useState<boolean>(false);
const [engine, setEngine] = useState<string>('');
const canSql = findPermission('can_sqllab', 'Superset', roles);
const canDashboard = findPermission('can_write', 'Dashboard', roles);
const canChart = findPermission('can_write', 'Chart', roles);
const canDatabase = findPermission('can_write', 'Database', roles);
const canDataset = findPermission('can_write', 'Dataset', roles);
const { canUploadData, canUploadCSV, canUploadColumnar, canUploadExcel } =
uploadUserPerms(
@ -135,7 +152,9 @@ const RightMenu = ({
);
const showActionDropdown = canSql || canChart || canDashboard;
const [allowUploads, setAllowUploads] = React.useState<boolean>(false);
const [allowUploads, setAllowUploads] = useState<boolean>(false);
const [nonExamplesDBConnected, setNonExamplesDBConnected] =
useState<boolean>(false);
const isAdmin = isUserAdmin(user);
const showUploads = allowUploads || isAdmin;
const dropdownItems: MenuObjectProps[] = [
@ -146,7 +165,12 @@ const RightMenu = ({
{
label: t('Connect database'),
name: GlobalMenuDataOptions.DB_CONNECTION,
perm: canDatabase,
perm: canDatabase && !nonExamplesDBConnected,
},
{
label: t('Create dataset'),
name: GlobalMenuDataOptions.DATASET_CREATION,
perm: canDataset && nonExamplesDBConnected,
},
{
label: t('Connect Google Sheet'),
@ -217,12 +241,29 @@ const RightMenu = ({
});
};
const existsNonExamplesDatabases = () => {
const payload = {
filters: [{ col: 'database_name', opr: 'neq', value: 'examples' }],
};
SupersetClient.get({
endpoint: `/api/v1/database/?q=${rison.encode(payload)}`,
}).then(({ json }: Record<string, any>) => {
setNonExamplesDBConnected(json.count >= 1);
});
};
useEffect(() => {
if (canUploadData) {
checkAllowUploads();
}
}, [canUploadData]);
useEffect(() => {
if (canDatabase || canDataset) {
existsNonExamplesDatabases();
}
}, [canDatabase, canDataset]);
const menuIconAndLabel = (menu: MenuObjectProps) => (
<>
<i data-test={`menu-item-${menu.label}`} className={`fa ${menu.icon}`} />
@ -232,16 +273,22 @@ const RightMenu = ({
const handleMenuSelection = (itemChose: any) => {
if (itemChose.key === GlobalMenuDataOptions.DB_CONNECTION) {
setShowModal(true);
setShowDatabaseModal(true);
} else if (itemChose.key === GlobalMenuDataOptions.GOOGLE_SHEETS) {
setShowModal(true);
setShowDatabaseModal(true);
setEngine('Google Sheets');
} else if (itemChose.key === GlobalMenuDataOptions.DATASET_CREATION) {
setShowDatasetModal(true);
}
};
const handleOnHideModal = () => {
setEngine('');
setShowModal(false);
setShowDatabaseModal(false);
};
const handleOnHideDatasetModalModal = () => {
setShowDatasetModal(false);
};
const isDisabled = isAdmin && !allowUploads;
@ -259,21 +306,33 @@ const RightMenu = ({
</Tooltip>
</Menu.Item>
) : (
<Menu.Item key={item.name}>
<Menu.Item key={item.name} css={styledChildMenu}>
{item.url ? <a href={item.url}> {item.label} </a> : item.label}
</Menu.Item>
);
};
const onMenuOpen = (openKeys: string[]) => {
if (openKeys.length && canUploadData) {
return checkAllowUploads();
// We should query the API only if opening Data submenus
// because the rest don't need this information. Not using
// "Data" directly since we might change the label later on?
if (
openKeys.length > 1 &&
!isEmpty(
openKeys?.filter((key: string) =>
key.includes(`sub2_${dropdownItems?.[0]?.label}`),
),
)
) {
if (canUploadData) checkAllowUploads();
if (canDatabase || canDataset) existsNonExamplesDatabases();
}
return null;
};
const RightMenuExtension = extensionsRegistry.get('navbar.right');
const handleDatabaseAdd = () => setQuery({ databaseAdded: true });
const handleDatasetAdd = () => setQuery({ datasetAdded: true });
const theme = useTheme();
@ -282,11 +341,18 @@ const RightMenu = ({
{canDatabase && (
<DatabaseModal
onHide={handleOnHideModal}
show={showModal}
show={showDatabaseModal}
dbEngine={engine}
onDatabaseAdd={handleDatabaseAdd}
/>
)}
{canDataset && (
<AddDatasetModal
onHide={handleOnHideDatasetModalModal}
show={showDatasetModal}
onDatasetAdd={handleDatasetAdd}
/>
)}
{environmentTag?.text && (
<Label
css={{ borderRadius: `${theme.gridUnit * 125}px` }}
@ -331,7 +397,7 @@ const RightMenu = ({
{menu?.childs?.map?.((item, idx) =>
typeof item !== 'string' && item.name && item.perm ? (
<Fragment key={item.name}>
{idx === 2 && <Menu.Divider />}
{idx === 3 && <Menu.Divider />}
{buildMenuItem(item)}
</Fragment>
) : null,
@ -486,6 +552,7 @@ const RightMenu = ({
const RightMenuWithQueryWrapper: React.FC<RightMenuProps> = props => {
const [, setQuery] = useQueryParams({
databaseAdded: BooleanParam,
datasetAdded: BooleanParam,
});
return <RightMenu setQuery={setQuery} {...props} />;

View File

@ -40,4 +40,5 @@ export interface RightMenuProps {
export enum GlobalMenuDataOptions {
GOOGLE_SHEETS = 'gsheets',
DB_CONNECTION = 'dbconnection',
DATASET_CREATION = 'datasetCreation',
}