Skip to content

Commit

Permalink
feat: allow uploads in crud view (#18953)
Browse files Browse the repository at this point in the history
* feat: allow uploads in crud view

* fix merge conflict and fix ts

* fix import

* fix tests

* fix lint

* remove unused var

* fix underline flash and alignment

* fix offset

* fix icon alignment

* fix labels and css issues

* make drowdown primary all the time

* make global

* fix lables

* add upload perms to utils

* remove unused code

* add suggested changes

* update menuright
  • Loading branch information
pkdotson committed Mar 21, 2022
1 parent 9ae51f7 commit d771ddb
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
import React from 'react';
import thunk from 'redux-thunk';
import * as redux from 'react-redux';
import configureStore from 'redux-mock-store';
import fetchMock from 'fetch-mock';
import { Provider } from 'react-redux';
Expand All @@ -40,6 +41,17 @@ import { act } from 'react-dom/test-utils';
const mockStore = configureStore([thunk]);
const store = mockStore({});

const mockAppState = {
common: {
config: {
CSV_EXTENSIONS: ['csv'],
EXCEL_EXTENSIONS: ['xls', 'xlsx'],
COLUMNAR_EXTENSIONS: ['parquet', 'zip'],
ALLOWED_EXTENSIONS: ['parquet', 'zip', 'xls', 'xlsx', 'csv'],
},
},
};

const databasesInfoEndpoint = 'glob:*/api/v1/database/_info*';
const databasesEndpoint = 'glob:*/api/v1/database/?*';
const databaseEndpoint = 'glob:*/api/v1/database/*';
Expand Down Expand Up @@ -94,12 +106,22 @@ fetchMock.get(databaseRelatedEndpoint, {
},
});

const useSelectorMock = jest.spyOn(redux, 'useSelector');

describe('DatabaseList', () => {
useSelectorMock.mockReturnValue({
CSV_EXTENSIONS: ['csv'],
EXCEL_EXTENSIONS: ['xls', 'xlsx'],
COLUMNAR_EXTENSIONS: ['parquet', 'zip'],
ALLOWED_EXTENSIONS: ['parquet', 'zip', 'xls', 'xlsx', 'csv'],
});

const wrapper = mount(
<Provider store={store}>
<DatabaseList user={mockUser} />
</Provider>,
);

beforeAll(async () => {
await waitForComponentToPaint(wrapper);
});
Expand Down Expand Up @@ -195,6 +217,7 @@ describe('RTL', () => {
<DatabaseList user={mockUser} />
</QueryParamProvider>,
{ useRedux: true },
mockAppState,
);
});

Expand Down
56 changes: 55 additions & 1 deletion superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@
*/
import { SupersetClient, t, styled } from '@superset-ui/core';
import React, { useState, useMemo } from 'react';
import { useSelector } from 'react-redux';
import Loading from 'src/components/Loading';
import { isFeatureEnabled, FeatureFlag } from 'src/featureFlags';
import { useListViewResource } from 'src/views/CRUD/hooks';
import { createErrorHandler } from 'src/views/CRUD/utils';
import { createErrorHandler, uploadUserPerms } from 'src/views/CRUD/utils';
import withToasts from 'src/components/MessageToasts/withToasts';
import SubMenu, { SubMenuProps } from 'src/views/components/SubMenu';
import DeleteModal from 'src/components/DeleteModal';
Expand All @@ -31,6 +32,8 @@ import ListView, { FilterOperator, Filters } from 'src/components/ListView';
import { commonMenuData } from 'src/views/CRUD/data/common';
import ImportModelsModal from 'src/components/ImportModal/index';
import handleResourceExport from 'src/utils/export';
import { ExtentionConfigs } from 'src/views/components/types';
import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes';
import DatabaseModal from './DatabaseModal';

import { DatabaseObject } from './types';
Expand Down Expand Up @@ -103,6 +106,15 @@ function DatabaseList({ addDangerToast, addSuccessToast }: DatabaseListProps) {
const [importingDatabase, showImportModal] = useState<boolean>(false);
const [passwordFields, setPasswordFields] = useState<string[]>([]);
const [preparingExport, setPreparingExport] = useState<boolean>(false);
const { roles } = useSelector<any, UserWithPermissionsAndRoles>(
state => state.user,
);
const {
CSV_EXTENSIONS,
COLUMNAR_EXTENSIONS,
EXCEL_EXTENSIONS,
ALLOWED_EXTENSIONS,
} = useSelector<any, ExtentionConfigs>(state => state.common.conf);

const openDatabaseImportModal = () => {
showImportModal(true);
Expand Down Expand Up @@ -171,8 +183,49 @@ function DatabaseList({ addDangerToast, addSuccessToast }: DatabaseListProps) {
const canExport =
hasPerm('can_export') && isFeatureEnabled(FeatureFlag.VERSIONED_EXPORT);

const { canUploadCSV, canUploadColumnar, canUploadExcel } = uploadUserPerms(
roles,
CSV_EXTENSIONS,
COLUMNAR_EXTENSIONS,
EXCEL_EXTENSIONS,
ALLOWED_EXTENSIONS,
);

const uploadDropdownMenu = [
{
label: t('Upload file to database'),
childs: [
{
label: t('Upload CSV'),
name: 'Upload CSV file',
url: '/csvtodatabaseview/form',
perm: canUploadCSV,
},
{
label: t('Upload columnar file'),
name: 'Upload columnar file',
url: '/columnartodatabaseview/form',
perm: canUploadColumnar,
},
{
label: t('Upload Excel file'),
name: 'Upload Excel file',
url: '/exceltodatabaseview/form',
perm: canUploadExcel,
},
],
},
];

const filteredDropDown = uploadDropdownMenu.map(link => {
// eslint-disable-next-line no-param-reassign
link.childs = link.childs.filter(item => item.perm);
return link;
});

const menuData: SubMenuProps = {
activeChild: 'Databases',
dropDownLinks: filteredDropDown,
...commonMenuData,
};

Expand Down Expand Up @@ -222,6 +275,7 @@ function DatabaseList({ addDangerToast, addSuccessToast }: DatabaseListProps) {
}

const initialSort = [{ id: 'changed_on_delta_humanized', desc: true }];

const columns = useMemo(
() => [
{
Expand Down
19 changes: 19 additions & 0 deletions superset-frontend/src/views/CRUD/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import rison from 'rison';
import { getClientErrorObject } from 'src/utils/getClientErrorObject';
import { FetchDataConfig } from 'src/components/ListView';
import SupersetText from 'src/utils/textUtils';
import findPermission from 'src/dashboard/util/findPermission';
import { Dashboard, Filters } from './types';

// Modifies the rison encoding slightly to match the backend's rison encoding/decoding. Applies globally.
Expand Down Expand Up @@ -420,3 +421,21 @@ export const checkUploadExtensions = (
}
return false;
};

export const uploadUserPerms = (
roles: Record<string, [string, string][]>,
csvExt: Array<string>,
colExt: Array<string>,
excelExt: Array<string>,
allowedExt: Array<string>,
) => ({
canUploadCSV:
findPermission('can_this_form_get', 'CsvToDatabaseView', roles) &&
checkUploadExtensions(csvExt, allowedExt),
canUploadColumnar:
checkUploadExtensions(colExt, allowedExt) &&
findPermission('can_this_form_get', 'ColumnarToDatabaseView', roles),
canUploadExcel:
checkUploadExtensions(excelExt, allowedExt) &&
findPermission('can_this_form_get', 'ExcelToDatabaseView', roles),
});
2 changes: 1 addition & 1 deletion superset-frontend/src/views/components/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export interface MenuProps {
isFrontendRoute?: (path?: string) => boolean;
}

interface MenuObjectChildProps {
export interface MenuObjectChildProps {
label: string;
name?: string;
icon?: string;
Expand Down
32 changes: 9 additions & 23 deletions superset-frontend/src/views/components/MenuRight.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { useSelector } from 'react-redux';
import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes';
import LanguagePicker from './LanguagePicker';
import DatabaseModal from '../CRUD/data/database/DatabaseModal';
import { checkUploadExtensions } from '../CRUD/utils';
import { uploadUserPerms } from '../CRUD/utils';
import {
ExtentionConfigs,
GlobalMenuDataOptions,
Expand Down Expand Up @@ -86,20 +86,12 @@ const RightMenu = ({
const canChart = findPermission('can_write', 'Chart', roles);
const canDatabase = findPermission('can_write', 'Database', roles);

const canUploadCSV = findPermission(
'can_this_form_get',
'CsvToDatabaseView',
roles,
);
const canUploadColumnar = findPermission(
'can_this_form_get',
'ColumnarToDatabaseView',
roles,
);
const canUploadExcel = findPermission(
'can_this_form_get',
'ExcelToDatabaseView',
const { canUploadCSV, canUploadColumnar, canUploadExcel } = uploadUserPerms(
roles,
CSV_EXTENSIONS,
COLUMNAR_EXTENSIONS,
EXCEL_EXTENSIONS,
ALLOWED_EXTENSIONS,
);

const canUpload = canUploadCSV || canUploadColumnar || canUploadExcel;
Expand All @@ -123,25 +115,19 @@ const RightMenu = ({
label: t('Upload CSV to database'),
name: 'Upload a CSV',
url: '/csvtodatabaseview/form',
perm:
checkUploadExtensions(CSV_EXTENSIONS, ALLOWED_EXTENSIONS) &&
canUploadCSV,
perm: canUploadCSV,
},
{
label: t('Upload columnar file to database'),
name: 'Upload a Columnar file',
url: '/columnartodatabaseview/form',
perm:
checkUploadExtensions(COLUMNAR_EXTENSIONS, ALLOWED_EXTENSIONS) &&
canUploadColumnar,
perm: canUploadColumnar,
},
{
label: t('Upload Excel file to database'),
name: 'Upload Excel',
url: '/exceltodatabaseview/form',
perm:
checkUploadExtensions(EXCEL_EXTENSIONS, ALLOWED_EXTENSIONS) &&
canUploadExcel,
perm: canUploadExcel,
},
],
},
Expand Down
22 changes: 21 additions & 1 deletion superset-frontend/src/views/components/SubMenu.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,19 @@ const mockedProps = {
usesRouter: false,
},
],
dropDownLinks: [
{
label: 'test a upload',
childs: [
{
label: 'Upload Test',
name: 'Upload Test',
url: '/test/form',
perm: true,
},
],
},
],
};

test('should render', () => {
Expand Down Expand Up @@ -74,6 +87,13 @@ test('should render all the tabs links', () => {
});
});

test('should render dropdownlinks', async () => {
render(<SubMenu {...mockedProps} />);
userEvent.hover(screen.getByText('test a upload'));
const label = await screen.findByText('test a upload');
expect(label).toBeInTheDocument();
});

test('should render the buttons', () => {
const mockFunc = jest.fn();
const buttons = [
Expand All @@ -94,7 +114,7 @@ test('should render the buttons', () => {
};
render(<SubMenu {...buttonsProps} />);
const testButton = screen.getByText(buttons[0].name);
expect(screen.getAllByRole('button')).toHaveLength(2);
expect(screen.getAllByRole('button')).toHaveLength(3);
userEvent.click(testButton);
expect(mockFunc).toHaveBeenCalled();
});
Loading

0 comments on commit d771ddb

Please sign in to comment.