-
Notifications
You must be signed in to change notification settings - Fork 14.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: DBC-UI Globally available across the app 🌎 #18722
Changes from 40 commits
681c20e
a4bbd5e
2ae8696
237263a
04d1ff9
1c0f383
760d8c6
717c5bb
2502a9a
123eff6
785f74b
757d502
2780d60
8052f1f
b68aa95
a6c0bbe
0e9abe9
70e7ba4
117261e
4fc95cd
dca3be1
b45184b
c42491d
9eac285
a3cc781
e64d6eb
6e9eb0c
9121e01
e9152e4
5b27726
9b2bf5f
56902d3
3af0404
e43fbd9
0f55d41
5af39b6
0507aaa
bd4a8bf
58d7607
b1aeaa4
e2b0570
192a7f4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,39 +16,47 @@ | |
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
import React from 'react'; | ||
import React, { useState } from 'react'; | ||
import { MainNav as Menu } from 'src/common/components'; | ||
import { t, styled, css, SupersetTheme } from '@superset-ui/core'; | ||
import { Link } from 'react-router-dom'; | ||
import Icons from 'src/components/Icons'; | ||
import findPermission from 'src/dashboard/util/findPermission'; | ||
import { useSelector } from 'react-redux'; | ||
import { | ||
UserWithPermissionsAndRoles, | ||
CommonBootstrapData, | ||
} from 'src/types/bootstrapTypes'; | ||
import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes'; | ||
import LanguagePicker from './LanguagePicker'; | ||
import { NavBarProps, MenuObjectProps } from './Menu'; | ||
import DatabaseModal from '../CRUD/data/database/DatabaseModal'; | ||
import { | ||
ExtentionConfigs, | ||
GlobalMenuDataOptions, | ||
RightMenuProps, | ||
} from './types'; | ||
import { MenuObjectProps } from './Menu'; | ||
|
||
export const dropdownItems: MenuObjectProps[] = [ | ||
{ | ||
label: t('Data'), | ||
icon: 'fa-database', | ||
childs: [ | ||
{ | ||
icon: 'fa-upload', | ||
label: t('Connect Database'), | ||
name: GlobalMenuDataOptions.DB_CONNECTION, | ||
}, | ||
{ | ||
label: t('Connect Google Sheet'), | ||
name: GlobalMenuDataOptions.GOOGLE_SHEETS, | ||
}, | ||
{ | ||
label: t('Upload a CSV'), | ||
name: 'Upload a CSV', | ||
url: '/csvtodatabaseview/form', | ||
}, | ||
{ | ||
icon: 'fa-upload', | ||
label: t('Upload a Columnar File'), | ||
name: 'Upload a Columnar file', | ||
url: '/columnartodatabaseview/form', | ||
}, | ||
{ | ||
icon: 'fa-upload', | ||
label: t('Upload Excel'), | ||
name: 'Upload Excel', | ||
url: '/exceltodatabaseview/form', | ||
|
@@ -107,13 +115,6 @@ const StyledAnchor = styled.a` | |
|
||
const { SubMenu } = Menu; | ||
|
||
interface RightMenuProps { | ||
align: 'flex-start' | 'flex-end'; | ||
settings: MenuObjectProps[]; | ||
navbarRight: NavBarProps; | ||
isFrontendRoute: (path?: string) => boolean; | ||
} | ||
|
||
const RightMenu = ({ | ||
align, | ||
settings, | ||
|
@@ -123,16 +124,22 @@ const RightMenu = ({ | |
const { roles } = useSelector<any, UserWithPermissionsAndRoles>( | ||
state => state.user, | ||
); | ||
// @ts-ignore | ||
const { CSV_EXTENSIONS, COLUMNAR_EXTENSIONS, EXCEL_EXTENSIONS } = useSelector< | ||
any, | ||
CommonBootstrapData | ||
>(state => state.common.conf); | ||
const [showModal, setShowModal] = useState<boolean>(false); | ||
const [engine, setEngine] = useState<string>(''); | ||
const { | ||
CSV_EXTENSIONS, | ||
COLUMNAR_EXTENSIONS, | ||
EXCEL_EXTENSIONS, | ||
SHOW_GLOBAL_GSHEETS, | ||
} = useSelector<any, ExtentionConfigs>(state => state.common.conf); | ||
|
||
// if user has any of these roles the dropdown will appear | ||
const configMap = { | ||
'Upload a CSV': CSV_EXTENSIONS, | ||
'Upload a Columnar file': COLUMNAR_EXTENSIONS, | ||
'Upload Excel': EXCEL_EXTENSIONS, | ||
[GlobalMenuDataOptions.GOOGLE_SHEETS]: SHOW_GLOBAL_GSHEETS, | ||
[GlobalMenuDataOptions.DB_CONNECTION]: true, // todo(hugh): add permission check here for database creation | ||
}; | ||
const canSql = findPermission('can_sqllab', 'Superset', roles); | ||
const canDashboard = findPermission('can_write', 'Dashboard', roles); | ||
|
@@ -144,9 +151,25 @@ const RightMenu = ({ | |
{menu.label} | ||
</> | ||
); | ||
|
||
const handleMenuSelection = (itemChose: any) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is |
||
if (itemChose.key === GlobalMenuDataOptions.DB_CONNECTION) { | ||
setShowModal(true); | ||
setEngine(''); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this to clear out the value if google sheets was selected at some point? If so, can we reset the value on modal close instead? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried moving this logic to the into component, but since we are managing the state in the upper component (MenuRight) it doesn't reset. Open to other strategies here, but was having a hard time getting it to work without reseting from the upper component There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's ok to leave the logic here for which engine was chosen, but maybe instead of clearing it out on menu selection, we can clear out the engine value on the dbmodal onHide event? |
||
} else if (itemChose.key === GlobalMenuDataOptions.GOOGLE_SHEETS) { | ||
setShowModal(true); | ||
setEngine('Google Sheets'); | ||
} | ||
}; | ||
|
||
return ( | ||
<StyledDiv align={align}> | ||
<Menu mode="horizontal"> | ||
<DatabaseModal | ||
onHide={() => setShowModal(false)} | ||
show={showModal} | ||
dbEngine={engine} | ||
/> | ||
<Menu selectable={false} mode="horizontal" onClick={handleMenuSelection}> | ||
{!navbarRight.user_is_anonymous && showActionDropdown && ( | ||
<SubMenu | ||
data-test="new-dropdown" | ||
|
@@ -163,13 +186,17 @@ const RightMenu = ({ | |
className="data-menu" | ||
title={menuIconAndLabel(menu)} | ||
> | ||
{menu.childs.map(item => | ||
{menu.childs.map((item, idx) => | ||
typeof item !== 'string' && | ||
item.name && | ||
configMap[item.name] === true ? ( | ||
<Menu.Item key={item.name}> | ||
<a href={item.url}> {item.label} </a> | ||
</Menu.Item> | ||
<> | ||
{idx === 2 && <Menu.Divider />} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe for future-proofing this, we can say There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if we go above idx > 1, this would render after every menu item after the first 2 items. We just want it to render it once right after the db connection buttons. Thats the one of the main reason i split out the db connection buttons from the config. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, I see the complication. Again I would suggest separating the concerns between the data at the top and the rendering at the bottom. You could add the logic to the data with something like:
and then you can render the divider if typeof item === 'string' There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is something we can discuss later. |
||
<Menu.Item key={item.name}> | ||
{item.url && <a href={item.url}> {item.label} </a>} | ||
{item.url === undefined && item.label} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit, but instead of two lines can you combine these by making it a ternary statement? |
||
</Menu.Item> | ||
</> | ||
) : null, | ||
)} | ||
</SubMenu> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/** | ||
* Licensed to the Apache Software Foundation (ASF) under one | ||
* or more contributor license agreements. See the NOTICE file | ||
* distributed with this work for additional information | ||
* regarding copyright ownership. The ASF licenses this file | ||
* to you under the Apache License, Version 2.0 (the | ||
* "License"); you may not use this file except in compliance | ||
* with the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
|
||
import { NavBarProps, MenuObjectProps } from './Menu'; | ||
|
||
export interface ExtentionConfigs { | ||
CSV_EXTENSIONS: Array<any>; | ||
COLUMNAR_EXTENSIONS: Array<any>; | ||
EXCEL_EXTENSIONS: Array<any>; | ||
SHOW_GLOBAL_GSHEETS: boolean; | ||
} | ||
export interface RightMenuProps { | ||
align: 'flex-start' | 'flex-end'; | ||
settings: MenuObjectProps[]; | ||
navbarRight: NavBarProps; | ||
isFrontendRoute: (path?: string) => boolean; | ||
} | ||
|
||
export enum GlobalMenuDataOptions { | ||
GOOGLE_SHEETS = 'gsheets', | ||
DB_CONNECTION = 'dbconnection', | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -62,6 +62,8 @@ | |
from superset.commands.exceptions import CommandException, CommandInvalidError | ||
from superset.connectors.sqla import models | ||
from superset.datasets.commands.exceptions import get_dataset_exist_error_msg | ||
from superset.db_engine_specs import get_available_engine_specs | ||
from superset.db_engine_specs.gsheets import GSheetsEngineSpec | ||
from superset.errors import ErrorLevel, SupersetError, SupersetErrorType | ||
from superset.exceptions import ( | ||
SupersetErrorException, | ||
|
@@ -366,6 +368,10 @@ def common_bootstrap_payload() -> Dict[str, Any]: | |
ReportRecipientType.EMAIL, | ||
] | ||
|
||
# verify client has google sheets installed | ||
available_specs = get_available_engine_specs() | ||
frontend_config["SHOW_GLOBAL_GSHEETS"] = bool(available_specs[GSheetsEngineSpec]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. bump of an earlier comment.. can we name this something more generic like |
||
|
||
bootstrap_data = { | ||
"flash_messages": messages, | ||
"conf": frontend_config, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instead of a separate map here, how about we move these values into the upper dropdownItems map with a key of something like
has_perm
? Then below instead ofconfigMap[item.name] === true
you can sayitem.has_perm