From 6487ee4514397a6e4590025f8c912d4dae9f8143 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Tue, 26 Apr 2022 15:41:35 -0500 Subject: [PATCH 01/57] Frontend implementation of create dataset from infobox --- .../superset-ui-chart-controls/src/types.ts | 5 + .../src/query/types/Datasource.ts | 4 + .../src/SqlLab/components/ResultSet/index.tsx | 29 +- .../components/DatasourcePanel/index.tsx | 258 +++++++++++++++++- .../components/ExploreViewContainer/index.jsx | 1 + 5 files changed, 275 insertions(+), 22 deletions(-) diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/types.ts b/superset-frontend/packages/superset-ui-chart-controls/src/types.ts index 53e3a198bd7ce..a5810c723d1fa 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/types.ts +++ b/superset-frontend/packages/superset-ui-chart-controls/src/types.ts @@ -65,6 +65,11 @@ export interface DatasourceMeta { granularity_sqla?: string; datasource_name: string | null; description: string | null; + sl_dataset: { + sl_table: any; + query: any; + saved_query: any; + }; } export interface ControlPanelState { diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/Datasource.ts b/superset-frontend/packages/superset-ui-core/src/query/types/Datasource.ts index 073bdf90c1b5d..9e1c3caadfc99 100644 --- a/superset-frontend/packages/superset-ui-core/src/query/types/Datasource.ts +++ b/superset-frontend/packages/superset-ui-core/src/query/types/Datasource.ts @@ -22,6 +22,10 @@ import { Metric } from './Metric'; export enum DatasourceType { Table = 'table', Druid = 'druid', + Query = 'query', + Dataset = 'sl_dataset', + SlTable = 'sl_table', + SavedQuery = 'saved_query', } /** diff --git a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx index 39c897c8d4e3c..0850ea069c630 100644 --- a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx +++ b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx @@ -50,12 +50,12 @@ import ExploreResultsButton from '../ExploreResultsButton'; import HighlightedSql from '../HighlightedSql'; import QueryStateLabel from '../QueryStateLabel'; -enum DatasetRadioState { +export enum DatasetRadioState { SAVE_NEW = 1, OVERWRITE_DATASET = 2, } -const EXPLORE_CHART_DEFAULT = { +export const EXPLORE_CHART_DEFAULT = { metrics: [], groupby: [], time_range: 'No filter', @@ -71,14 +71,14 @@ enum LIMITING_FACTOR { const LOADING_STYLES: CSSProperties = { position: 'relative', minHeight: 100 }; -interface DatasetOwner { +export interface DatasetOwner { first_name: string; id: number; last_name: string; username: string; } -interface DatasetOptionAutocomplete { +export interface DatasetOptionAutocomplete { value: string; datasetId: number; owners: [DatasetOwner]; @@ -145,20 +145,7 @@ const ResultSetErrorMessage = styled.div` padding-top: ${({ theme }) => 4 * theme.gridUnit}px; `; -const ResultSetRowsReturned = styled.span` - white-space: nowrap; - text-overflow: ellipsis; - width: 100%; - overflow: hidden; - display: inline-block; -`; - -const LimitMessage = styled.span` - color: ${({ theme }) => theme.colors.secondary.light1}; - margin-left: ${({ theme }) => theme.gridUnit * 2}px; -`; - -const updateDataset = async ( +export const updateDataset = async ( dbId: number, datasetId: number, sql: string, @@ -646,10 +633,10 @@ export default class ResultSet extends React.PureComponent< return ( {!limitReached && !shouldUseDefaultDropdownAlert && ( - + {rowsReturnedMessage} - {limitMessage} - + {limitMessage} + )} {!limitReached && shouldUseDefaultDropdownAlert && (
diff --git a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx index 8bd39aa52f02c..40181c1f1d0de 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx @@ -17,7 +17,7 @@ * under the License. */ import React, { useEffect, useMemo, useRef, useState } from 'react'; -import { css, styled, t } from '@superset-ui/core'; +import { css, styled, t, makeApi, DatasourceType } from '@superset-ui/core'; import { ControlConfig, DatasourceMeta, @@ -26,6 +26,19 @@ import { import { debounce } from 'lodash'; import { matchSorter, rankings } from 'match-sorter'; import Collapse from 'src/components/Collapse'; +import Alert from 'src/components/Alert'; +import { SaveDatasetModal } from 'src/SqlLab/components/SaveDatasetModal'; +import { exploreChart } from 'src/explore/exploreUtils'; +import moment from 'moment'; +import rison from 'rison'; +import { + DatasetRadioState, + EXPLORE_CHART_DEFAULT, + DatasetOwner, + DatasetOptionAutocomplete, + updateDataset, +} from 'src/SqlLab/components/ResultSet'; +import { RadioChangeEvent } from 'src/components'; import { Input } from 'src/components/Input'; import { FAST_DEBOUNCE } from 'src/constants'; import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags'; @@ -49,6 +62,9 @@ export interface Props { actions: Partial & Pick; // we use this props control force update when this panel resize shouldForceUpdate?: number; + user: { + userId: number; + }; } const enableExploreDnd = isFeatureEnabled( @@ -174,6 +190,7 @@ export default function DataSourcePanel({ controls: { datasource: datasourceControl }, actions, shouldForceUpdate, + user, }: Props) { const { columns: _columns, metrics } = datasource; @@ -192,6 +209,35 @@ export default function DataSourcePanel({ [_columns], ); + const placeholderSlDataset = { + sl_table: [], + query: [], + saved_query: [], + }; + + // eslint-disable-next-line no-param-reassign + datasource.sl_dataset = placeholderSlDataset; + + const getDefaultDatasetName = () => + `${datasource.sl_dataset.query.tab} ${moment().format( + 'MM/DD/YYYY HH:mm:ss', + )}`; + + const [showSaveDatasetModal, setShowSaveDatasetModal] = useState(false); + const [newSaveDatasetName, setNewSaveDatasetName] = useState( + getDefaultDatasetName(), + ); + const [saveDatasetRadioBtnState, setSaveDatasetRadioBtnState] = useState( + DatasetRadioState.SAVE_NEW, + ); + const [shouldOverwriteDataSet, setShouldOverwriteDataSet] = useState(false); + const [userDatasetOptions, setUserDatasetOptions] = useState< + DatasetOptionAutocomplete[] + >([]); + const [datasetToOverwrite, setDatasetToOverwrite] = useState< + Record + >({}); + const [saveModalAutocompleteValue] = useState(''); const [inputValue, setInputValue] = useState(''); const [lists, setList] = useState({ columns, @@ -279,6 +325,7 @@ export default function DataSourcePanel({ : lists.metrics.slice(0, DEFAULT_MAX_METRICS_LENGTH), [lists.metrics, showAllMetrics], ); + const columnSlice = useMemo( () => showAllColumns @@ -289,6 +336,175 @@ export default function DataSourcePanel({ [lists.columns, showAllColumns], ); + const handleOverwriteDataset = async () => { + const { sql, results, dbId } = datasource.sl_dataset.query; + + await updateDataset( + dbId, + datasetToOverwrite.datasetId, + sql, + // TODO: lyndsiWilliams - Define d + results.selected_columns.map((d: any) => ({ + column_name: d.name, + type: d.type, + is_dttm: d.is_dttm, + })), + datasetToOverwrite.owners.map((o: DatasetOwner) => o.id), + true, + ); + + setShowSaveDatasetModal(false); + setShouldOverwriteDataSet(false); + setDatasetToOverwrite({}); + setNewSaveDatasetName(getDefaultDatasetName()); + + exploreChart({ + ...EXPLORE_CHART_DEFAULT, + datasource: `${datasetToOverwrite.datasetId}__table`, + // TODO: lyndsiWilliams - Define d + all_columns: results.selected_columns.map((d: any) => d.name), + }); + }; + + const getUserDatasets = async (searchText = '') => { + // Making sure that autocomplete input has a value before rendering the dropdown + // Transforming the userDatasetsOwned data for SaveModalComponent) + const { userId } = user; + if (userId) { + const queryParams = rison.encode({ + filters: [ + { + col: 'table_name', + opr: 'ct', + value: searchText, + }, + { + col: 'owners', + opr: 'rel_m_m', + value: userId, + }, + ], + order_column: 'changed_on_delta_humanized', + order_direction: 'desc', + }); + + const response = await makeApi({ + method: 'GET', + endpoint: '/api/v1/dataset', + })(`q=${queryParams}`); + + return response.result.map( + (r: { table_name: string; id: number; owners: [DatasetOwner] }) => ({ + value: r.table_name, + datasetId: r.id, + owners: r.owners, + }), + ); + } + + return null; + }; + + const handleSaveDatasetModalSearch = async (searchText: string) => { + const userDatasetsOwned = await getUserDatasets(searchText); + setUserDatasetOptions(userDatasetsOwned); + }; + + const handleSaveInDataset = () => { + // if user wants to overwrite a dataset we need to prompt them + if (saveDatasetRadioBtnState === DatasetRadioState.OVERWRITE_DATASET) { + setShouldOverwriteDataSet(true); + return; + } + + // TODO: lyndsiWilliams - set up when the back end logic is implemented + // const { schema, sql, dbId } = datasource.sl_dataset.query; + let { templateParams } = datasource.sl_dataset.query; + // const selectedColumns = + // datasource.sl_dataset.query?.results?.selected_columns || []; + + // The filters param is only used to test jinja templates. + // Remove the special filters entry from the templateParams + // before saving the dataset. + if (templateParams) { + const p = JSON.parse(templateParams); + /* eslint-disable-next-line no-underscore-dangle */ + if (p._filters) { + /* eslint-disable-next-line no-underscore-dangle */ + delete p._filters; + templateParams = JSON.stringify(p); + } + } + + // TODO: lyndsiWilliams - set up when the back end logic is implemented + // createDatasource({ + // schema, + // sql, + // dbId, + // templateParams, + // datasourceName: newSaveDatasetName, + // columns: selectedColumns, + // }); + // .then((data: { table_id: number }) => { + // exploreChart({ + // datasource: `${data.table_id}__table`, + // metrics: [], + // groupby: [], + // time_range: 'No filter', + // viz_type: 'table', + // all_columns: selectedColumns.map((c) => c.name), + // row_limit: 1000, + // }); + // }) + // .catch(() => { + // actions.addDangerToast(t('An error occurred saving dataset')); + // }); + + setShowSaveDatasetModal(false); + setNewSaveDatasetName(getDefaultDatasetName()); + }; + + const handleOverwriteDatasetOption = ( + _data: string, + option: Record, + ) => setDatasetToOverwrite(option); + + const handleDatasetNameChange = (e: React.FormEvent) => { + // @ts-expect-error + setNewSaveDatasetName(e.target.value); + }; + + const handleHideSaveModal = () => { + setShowSaveDatasetModal(false); + setShouldOverwriteDataSet(false); + }; + + const handleSaveDatasetRadioBtnState = (e: RadioChangeEvent) => { + setSaveDatasetRadioBtnState(Number(e.target.value)); + }; + + const handleOverwriteCancel = () => { + setShouldOverwriteDataSet(false); + setDatasetToOverwrite({}); + }; + + const disableSaveAndExploreBtn = + (saveDatasetRadioBtnState === DatasetRadioState.SAVE_NEW && + newSaveDatasetName.length === 0) || + (saveDatasetRadioBtnState === DatasetRadioState.OVERWRITE_DATASET && + Object.keys(datasetToOverwrite).length === 0 && + saveModalAutocompleteValue.length === 0); + + const handleFilterAutocompleteOption = ( + inputValue: string, + option: { value: string; datasetId: number }, + ) => option.value.toLowerCase().includes(inputValue.toLowerCase()); + + const showInfoboxCheck = () => { + if (sessionStorage.getItem('showInfobox') === 'false') return false; + return true; + }; + const mainBody = useMemo( () => ( <> @@ -303,6 +519,28 @@ export default function DataSourcePanel({ placeholder={t('Search Metrics & Columns')} />
+ {datasource.type === DatasourceType.Table && showInfoboxCheck() && ( + sessionStorage.setItem('showInfobox', 'false')} + type="info" + message="" + description={ + <> + setShowSaveDatasetModal(true)} + className="add-dataset-alert-description" + css={{ textDecoration: 'underline' }} + > + {t('Create a dataset')} + + {t(' to edit or add columns and metrics.')} + + } + /> + )} + setDatasetToOverwrite({})} + /> {datasource.id != null && mainBody} diff --git a/superset-frontend/src/explore/components/ExploreViewContainer/index.jsx b/superset-frontend/src/explore/components/ExploreViewContainer/index.jsx index bf8a17a3650cc..79d372122003b 100644 --- a/superset-frontend/src/explore/components/ExploreViewContainer/index.jsx +++ b/superset-frontend/src/explore/components/ExploreViewContainer/index.jsx @@ -596,6 +596,7 @@ function ExploreViewContainer(props) { controls={props.controls} actions={props.actions} shouldForceUpdate={shouldForceUpdate} + user={props.user} /> {isCollapsed ? ( From 2675ccbafcb0c410cd2402a2eff337b09971a54a Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Tue, 26 Apr 2022 16:02:48 -0500 Subject: [PATCH 02/57] Fixed sl_dataset type --- .../src/explore/components/DatasourcePanel/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx index 40181c1f1d0de..438e5316bbbf4 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx @@ -219,7 +219,7 @@ export default function DataSourcePanel({ datasource.sl_dataset = placeholderSlDataset; const getDefaultDatasetName = () => - `${datasource.sl_dataset.query.tab} ${moment().format( + `${datasource?.sl_dataset?.query.tab} ${moment().format( 'MM/DD/YYYY HH:mm:ss', )}`; @@ -337,7 +337,7 @@ export default function DataSourcePanel({ ); const handleOverwriteDataset = async () => { - const { sql, results, dbId } = datasource.sl_dataset.query; + const { sql, results, dbId } = datasource?.sl_dataset?.query; await updateDataset( dbId, @@ -419,7 +419,7 @@ export default function DataSourcePanel({ // TODO: lyndsiWilliams - set up when the back end logic is implemented // const { schema, sql, dbId } = datasource.sl_dataset.query; - let { templateParams } = datasource.sl_dataset.query; + let { templateParams } = datasource?.sl_dataset?.query; // const selectedColumns = // datasource.sl_dataset.query?.results?.selected_columns || []; From 0bbb69fdbce56dfc1fac8932454dcdd4cb7a39cc Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Tue, 26 Apr 2022 16:05:47 -0500 Subject: [PATCH 03/57] Fix test --- .../components/DatasourcePanel/DatasourcePanel.test.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx b/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx index 99c596b80ed9c..dafc858f7076d 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx @@ -78,6 +78,9 @@ const props: DatasourcePanelProps = { actions: { setControlValue: jest.fn(), }, + user: { + userId: 1, + }, }; const setup = (props: DatasourcePanelProps) => ( From a5688f5b876d4d075771d21a87911e3ec1748474 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Tue, 26 Apr 2022 16:10:38 -0500 Subject: [PATCH 04/57] Fixed sl_dataset type (forgot to save) --- .../packages/superset-ui-chart-controls/src/types.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/types.ts b/superset-frontend/packages/superset-ui-chart-controls/src/types.ts index a5810c723d1fa..5b39748dbd972 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/types.ts +++ b/superset-frontend/packages/superset-ui-chart-controls/src/types.ts @@ -65,10 +65,10 @@ export interface DatasourceMeta { granularity_sqla?: string; datasource_name: string | null; description: string | null; - sl_dataset: { - sl_table: any; - query: any; - saved_query: any; + sl_dataset?: { + sl_table?: any; + query?: any; + saved_query?: any; }; } From 8a3e4aa8b489dd501ab65cf29f7e487e0cb5fe21 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Tue, 26 Apr 2022 17:16:20 -0500 Subject: [PATCH 05/57] RTL testing --- .../DatasourcePanel/DatasourcePanel.test.tsx | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx b/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx index dafc858f7076d..748cfdd893c2a 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx @@ -182,3 +182,29 @@ test('should render a warning', async () => { await screen.findByRole('img', { name: 'alert-solid' }), ).toBeInTheDocument(); }); + +test('should render a create dataset infobox', () => { + render(setup(props)); + + const createButton = screen.getByRole('button', { + name: /create a dataset/i, + }); + const infoboxText = screen.getByText(/to edit or add columns and metrics./i); + + expect(createButton).toBeVisible(); + expect(infoboxText).toBeVisible(); +}); + +test('should render a save dataset modal when "Create a dataset" is clicked', () => { + render(setup(props)); + + const createButton = screen.getByRole('button', { + name: /create a dataset/i, + }); + + userEvent.click(createButton); + + const saveDatasetModalTitle = screen.getByText(/save or overwrite dataset/i); + + expect(saveDatasetModalTitle).toBeVisible(); +}); From 3e1f9136c7775f9a9dfff5fb52b53761c087ed3c Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Tue, 26 Apr 2022 18:09:15 -0500 Subject: [PATCH 06/57] Adjusted styling/text on infobox and save dataset modal --- .../src/SqlLab/components/ResultSet/index.tsx | 5 ++ .../components/SaveDatasetModal/index.tsx | 26 +++++--- .../components/DatasourcePanel/index.tsx | 62 ++++++++++++------- 3 files changed, 62 insertions(+), 31 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx index 0850ea069c630..1f8efe04adb04 100644 --- a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx +++ b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx @@ -525,6 +525,11 @@ export default class ResultSet extends React.PureComponent< handleSaveDatasetModalSearch={this.handleSaveDatasetModalSearch} filterAutocompleteOption={this.handleFilterAutocompleteOption} onChangeAutoComplete={this.handleOnChangeAutoComplete} + buttonTextOnSave={t('Save & Explore')} + buttonTextOnOverwrite={t('Overwrite & Explore')} + modalDescription={t( + 'Save this query as a virtual dataset to continue exploring', + )} /> {this.props.visualize && diff --git a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx index 21884dbe8f13b..305402eb85786 100644 --- a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx +++ b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx @@ -49,6 +49,9 @@ interface SaveDatasetModalProps { saveDatasetRadioBtnState: number; shouldOverwriteDataset: boolean; userDatasetOptions: AutoCompleteProps['options']; + buttonTextOnSave: string; + buttonTextOnOverwrite: string; + modalDescription?: string; } const Styles = styled.div` @@ -92,10 +95,13 @@ export const SaveDatasetModal: FunctionComponent = ({ filterAutocompleteOption, userDatasetOptions, onChangeAutoComplete, + buttonTextOnSave, + buttonTextOnOverwrite, + modalDescription, }) => ( @@ -105,7 +111,7 @@ export const SaveDatasetModal: FunctionComponent = ({ buttonStyle="primary" onClick={onOk} > - {t('Save & Explore')} + {buttonTextOnSave} )} {shouldOverwriteDataset && ( @@ -117,7 +123,7 @@ export const SaveDatasetModal: FunctionComponent = ({ onClick={handleOverwriteDataset} disabled={disableSaveAndExploreBtn} > - {t('Overwrite & Explore')} + {buttonTextOnOverwrite} )} @@ -127,15 +133,15 @@ export const SaveDatasetModal: FunctionComponent = ({ {!shouldOverwriteDataset && (
-
- Save this query as a virtual dataset to continue exploring -
+ {modalDescription && ( +
{modalDescription}
+ )} - Save as new + {t('Save as new')} = ({ /> - Overwrite existing + {t('Overwrite existing')} @@ -161,7 +167,7 @@ export const SaveDatasetModal: FunctionComponent = ({ )} {shouldOverwriteDataset && (
- Are you sure you want to overwrite this dataset? + {t('Are you sure you want to overwrite this dataset?')}
)} diff --git a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx index 438e5316bbbf4..98d88a011d8fb 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx @@ -17,7 +17,14 @@ * under the License. */ import React, { useEffect, useMemo, useRef, useState } from 'react'; -import { css, styled, t, makeApi, DatasourceType } from '@superset-ui/core'; +import { + css, + styled, + t, + makeApi, + DatasourceType, + SupersetTheme, +} from '@superset-ui/core'; import { ControlConfig, DatasourceMeta, @@ -170,6 +177,16 @@ const SectionHeader = styled.span` `} `; +const StyledInfoboxWrapper = styled.div` + ${({ theme }) => css` + margin: 0 ${theme.gridUnit * 2.5}; + + span { + text-decoration: underline; + } + `} +`; + const LabelContainer = (props: { children: React.ReactElement; className: string; @@ -520,26 +537,27 @@ export default function DataSourcePanel({ />
{datasource.type === DatasourceType.Table && showInfoboxCheck() && ( - sessionStorage.setItem('showInfobox', 'false')} - type="info" - message="" - description={ - <> - setShowSaveDatasetModal(true)} - className="add-dataset-alert-description" - css={{ textDecoration: 'underline' }} - > - {t('Create a dataset')} - - {t(' to edit or add columns and metrics.')} - - } - /> + + sessionStorage.setItem('showInfobox', 'false')} + type="info" + message="" + description={ + <> + setShowSaveDatasetModal(true)} + className="add-dataset-alert-description" + > + {t('Create a dataset')} + + {t(' to edit or add columns and metrics.')} + + } + /> + )} setDatasetToOverwrite({})} + buttonTextOnSave={t('Save')} + buttonTextOnOverwrite={t('Overwrite')} /> {datasource.id != null && mainBody} From d4afdfe5176b287fa200950ad6b55d03ec49e5f9 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Tue, 26 Apr 2022 18:17:40 -0500 Subject: [PATCH 07/57] Appease lint --- .../src/explore/components/DatasourcePanel/index.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx index 98d88a011d8fb..61fe3408a2a9e 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx @@ -17,14 +17,7 @@ * under the License. */ import React, { useEffect, useMemo, useRef, useState } from 'react'; -import { - css, - styled, - t, - makeApi, - DatasourceType, - SupersetTheme, -} from '@superset-ui/core'; +import { css, styled, t, makeApi, DatasourceType } from '@superset-ui/core'; import { ControlConfig, DatasourceMeta, From c32412bf0bc632a5aee6da695aa672190bf571c9 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Wed, 27 Apr 2022 12:11:37 -0500 Subject: [PATCH 08/57] Make infobox invisible and fix tests --- .../DatasourcePanel/DatasourcePanel.test.tsx | 20 +++++++- .../components/DatasourcePanel/index.tsx | 51 ++++++++++--------- 2 files changed, 46 insertions(+), 25 deletions(-) diff --git a/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx b/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx index 748cfdd893c2a..a6e84b54ec447 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx @@ -184,7 +184,15 @@ test('should render a warning', async () => { }); test('should render a create dataset infobox', () => { - render(setup(props)); + render( + setup({ + ...props, + datasource: { + ...datasource, + type: DatasourceType.SavedQuery, + }, + }), + ); const createButton = screen.getByRole('button', { name: /create a dataset/i, @@ -196,7 +204,15 @@ test('should render a create dataset infobox', () => { }); test('should render a save dataset modal when "Create a dataset" is clicked', () => { - render(setup(props)); + render( + setup({ + ...props, + datasource: { + ...datasource, + type: DatasourceType.SavedQuery, + }, + }), + ); const createButton = screen.getByRole('button', { name: /create a dataset/i, diff --git a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx index 61fe3408a2a9e..a7c42c3603eab 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx @@ -529,29 +529,34 @@ export default function DataSourcePanel({ placeholder={t('Search Metrics & Columns')} />
- {datasource.type === DatasourceType.Table && showInfoboxCheck() && ( - - sessionStorage.setItem('showInfobox', 'false')} - type="info" - message="" - description={ - <> - setShowSaveDatasetModal(true)} - className="add-dataset-alert-description" - > - {t('Create a dataset')} - - {t(' to edit or add columns and metrics.')} - - } - /> - - )} + {datasource.type === DatasourceType.Dataset || + datasource.type === DatasourceType.SlTable || + (datasource.type === DatasourceType.SavedQuery && + showInfoboxCheck() && ( + + + sessionStorage.setItem('showInfobox', 'false') + } + type="info" + message="" + description={ + <> + setShowSaveDatasetModal(true)} + className="add-dataset-alert-description" + > + {t('Create a dataset')} + + {t(' to edit or add columns and metrics.')} + + } + /> + + ))} Date: Wed, 27 Apr 2022 14:12:37 -0500 Subject: [PATCH 09/57] Remove unnecessary placeholder --- .../components/DatasourcePanel/index.tsx | 60 +++++++------------ 1 file changed, 23 insertions(+), 37 deletions(-) diff --git a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx index a7c42c3603eab..2c3770e308a51 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx @@ -219,15 +219,6 @@ export default function DataSourcePanel({ [_columns], ); - const placeholderSlDataset = { - sl_table: [], - query: [], - saved_query: [], - }; - - // eslint-disable-next-line no-param-reassign - datasource.sl_dataset = placeholderSlDataset; - const getDefaultDatasetName = () => `${datasource?.sl_dataset?.query.tab} ${moment().format( 'MM/DD/YYYY HH:mm:ss', @@ -529,34 +520,29 @@ export default function DataSourcePanel({ placeholder={t('Search Metrics & Columns')} />
- {datasource.type === DatasourceType.Dataset || - datasource.type === DatasourceType.SlTable || - (datasource.type === DatasourceType.SavedQuery && - showInfoboxCheck() && ( - - - sessionStorage.setItem('showInfobox', 'false') - } - type="info" - message="" - description={ - <> - setShowSaveDatasetModal(true)} - className="add-dataset-alert-description" - > - {t('Create a dataset')} - - {t(' to edit or add columns and metrics.')} - - } - /> - - ))} + {datasource.type === DatasourceType.Table && showInfoboxCheck() && ( + + sessionStorage.setItem('showInfobox', 'false')} + type="info" + message="" + description={ + <> + setShowSaveDatasetModal(true)} + className="add-dataset-alert-description" + > + {t('Create a dataset')} + + {t(' to edit or add columns and metrics.')} + + } + /> + + )} Date: Wed, 27 Apr 2022 14:47:05 -0500 Subject: [PATCH 10/57] Move types to sql lab --- .../src/SqlLab/components/ResultSet/index.tsx | 10 +- superset-frontend/src/SqlLab/types.ts | 25 +++++ .../components/DatasourcePanel/index.tsx | 99 +++++++++++++------ 3 files changed, 98 insertions(+), 36 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx index 1f8efe04adb04..a743d024192d9 100644 --- a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx +++ b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx @@ -50,12 +50,12 @@ import ExploreResultsButton from '../ExploreResultsButton'; import HighlightedSql from '../HighlightedSql'; import QueryStateLabel from '../QueryStateLabel'; -export enum DatasetRadioState { +enum DatasetRadioState { SAVE_NEW = 1, OVERWRITE_DATASET = 2, } -export const EXPLORE_CHART_DEFAULT = { +const EXPLORE_CHART_DEFAULT = { metrics: [], groupby: [], time_range: 'No filter', @@ -71,14 +71,14 @@ enum LIMITING_FACTOR { const LOADING_STYLES: CSSProperties = { position: 'relative', minHeight: 100 }; -export interface DatasetOwner { +interface DatasetOwner { first_name: string; id: number; last_name: string; username: string; } -export interface DatasetOptionAutocomplete { +interface DatasetOptionAutocomplete { value: string; datasetId: number; owners: [DatasetOwner]; @@ -145,7 +145,7 @@ const ResultSetErrorMessage = styled.div` padding-top: ${({ theme }) => 4 * theme.gridUnit}px; `; -export const updateDataset = async ( +const updateDataset = async ( dbId: number, datasetId: number, sql: string, diff --git a/superset-frontend/src/SqlLab/types.ts b/superset-frontend/src/SqlLab/types.ts index e1714791638c8..8c3235bcaf7ba 100644 --- a/superset-frontend/src/SqlLab/types.ts +++ b/superset-frontend/src/SqlLab/types.ts @@ -128,3 +128,28 @@ export type RootState = { messageToasts: toastState[]; common: {}; }; + +export enum DatasetRadioState { + SAVE_NEW = 1, + OVERWRITE_DATASET = 2, +} + +export const EXPLORE_CHART_DEFAULT = { + metrics: [], + groupby: [], + time_range: 'No filter', + viz_type: 'table', +}; + +export interface DatasetOwner { + first_name: string; + id: number; + last_name: string; + username: string; +} + +export interface DatasetOptionAutocomplete { + value: string; + datasetId: number; + owners: [DatasetOwner]; +} diff --git a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx index 2c3770e308a51..cbbfc8dfc6aa8 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx @@ -17,7 +17,15 @@ * under the License. */ import React, { useEffect, useMemo, useRef, useState } from 'react'; -import { css, styled, t, makeApi, DatasourceType } from '@superset-ui/core'; +import { + css, + styled, + t, + makeApi, + SupersetClient, + JsonResponse, + DatasourceType, +} from '@superset-ui/core'; import { ControlConfig, DatasourceMeta, @@ -31,13 +39,6 @@ import { SaveDatasetModal } from 'src/SqlLab/components/SaveDatasetModal'; import { exploreChart } from 'src/explore/exploreUtils'; import moment from 'moment'; import rison from 'rison'; -import { - DatasetRadioState, - EXPLORE_CHART_DEFAULT, - DatasetOwner, - DatasetOptionAutocomplete, - updateDataset, -} from 'src/SqlLab/components/ResultSet'; import { RadioChangeEvent } from 'src/components'; import { Input } from 'src/components/Input'; import { FAST_DEBOUNCE } from 'src/constants'; @@ -45,6 +46,12 @@ import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags'; import { ExploreActions } from 'src/explore/actions/exploreActions'; import Control from 'src/explore/components/Control'; import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes'; +import { + DatasetRadioState, + EXPLORE_CHART_DEFAULT, + DatasetOwner, + DatasetOptionAutocomplete, +} from 'src/SqlLab/types'; import DatasourcePanelDragOption from './DatasourcePanelDragOption'; import { DndItemType } from '../DndItemType'; import { StyledColumnOption, StyledMetricOption } from '../optionRenderers'; @@ -180,6 +187,31 @@ const StyledInfoboxWrapper = styled.div` `} `; +const updateDataset = async ( + dbId: number, + datasetId: number, + sql: string, + columns: Array>, + owners: [number], + overrideColumns: boolean, +) => { + const endpoint = `api/v1/dataset/${datasetId}?override_columns=${overrideColumns}`; + const headers = { 'Content-Type': 'application/json' }; + const body = JSON.stringify({ + sql, + columns, + owners, + database_id: dbId, + }); + + const data: JsonResponse = await SupersetClient.put({ + endpoint, + headers, + body, + }); + return data.json.result; +}; + const LabelContainer = (props: { children: React.ReactElement; className: string; @@ -520,29 +552,34 @@ export default function DataSourcePanel({ placeholder={t('Search Metrics & Columns')} />
- {datasource.type === DatasourceType.Table && showInfoboxCheck() && ( - - sessionStorage.setItem('showInfobox', 'false')} - type="info" - message="" - description={ - <> - setShowSaveDatasetModal(true)} - className="add-dataset-alert-description" - > - {t('Create a dataset')} - - {t(' to edit or add columns and metrics.')} - - } - /> - - )} + {datasource.type === DatasourceType.Dataset || + datasource.type === DatasourceType.SlTable || + (datasource.type === DatasourceType.SavedQuery && + showInfoboxCheck() && ( + + + sessionStorage.setItem('showInfobox', 'false') + } + type="info" + message="" + description={ + <> + setShowSaveDatasetModal(true)} + className="add-dataset-alert-description" + > + {t('Create a dataset')} + + {t(' to edit or add columns and metrics.')} + + } + /> + + ))} Date: Mon, 2 May 2022 17:47:19 -0500 Subject: [PATCH 11/57] Moved logic into save dataset modal --- .../src/SqlLab/components/ResultSet/index.tsx | 323 +------------- .../components/SaveDatasetModal/index.tsx | 407 +++++++++++++----- .../components/DatasourcePanel/index.tsx | 245 +---------- 3 files changed, 313 insertions(+), 662 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx index a743d024192d9..e10a42e3c52ea 100644 --- a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx +++ b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx @@ -19,19 +19,9 @@ import React, { CSSProperties } from 'react'; import ButtonGroup from 'src/components/ButtonGroup'; import Alert from 'src/components/Alert'; -import moment from 'moment'; -import { RadioChangeEvent } from 'src/components'; import Button from 'src/components/Button'; import shortid from 'shortid'; -import rison from 'rison'; -import { - styled, - t, - makeApi, - SupersetClient, - JsonResponse, -} from '@superset-ui/core'; -import { debounce } from 'lodash'; +import { styled, t } from '@superset-ui/core'; import ErrorMessageWithStackTrace from 'src/components/ErrorMessage/ErrorMessageWithStackTrace'; import { SaveDatasetModal } from 'src/SqlLab/components/SaveDatasetModal'; import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes'; @@ -42,7 +32,6 @@ import FilterableTable, { } from 'src/components/FilterableTable'; import CopyToClipboard from 'src/components/CopyToClipboard'; import { prepareCopyToClipboardTabularData } from 'src/utils/common'; -import { exploreChart } from 'src/explore/exploreUtils'; import { CtasEnum } from 'src/SqlLab/actions/sqlLab'; import { Query } from 'src/SqlLab/types'; import ExploreCtasResultsButton from '../ExploreCtasResultsButton'; @@ -50,18 +39,6 @@ import ExploreResultsButton from '../ExploreResultsButton'; import HighlightedSql from '../HighlightedSql'; import QueryStateLabel from '../QueryStateLabel'; -enum DatasetRadioState { - SAVE_NEW = 1, - OVERWRITE_DATASET = 2, -} - -const EXPLORE_CHART_DEFAULT = { - metrics: [], - groupby: [], - time_range: 'No filter', - viz_type: 'table', -}; - enum LIMITING_FACTOR { QUERY = 'QUERY', QUERY_AND_DROPDOWN = 'QUERY_AND_DROPDOWN', @@ -71,19 +48,6 @@ enum LIMITING_FACTOR { const LOADING_STYLES: CSSProperties = { position: 'relative', minHeight: 100 }; -interface DatasetOwner { - first_name: string; - id: number; - last_name: string; - username: string; -} - -interface DatasetOptionAutocomplete { - value: string; - datasetId: number; - owners: [DatasetOwner]; -} - interface ResultSetProps { showControls?: boolean; actions: Record; @@ -105,12 +69,6 @@ interface ResultSetState { showExploreResultsButton: boolean; data: Record[]; showSaveDatasetModal: boolean; - newSaveDatasetName: string; - saveDatasetRadioBtnState: number; - shouldOverwriteDataSet: boolean; - datasetToOverwrite: Record; - saveModalAutocompleteValue: string; - userDatasetOptions: DatasetOptionAutocomplete[]; alertIsOpen: boolean; } @@ -145,31 +103,6 @@ const ResultSetErrorMessage = styled.div` padding-top: ${({ theme }) => 4 * theme.gridUnit}px; `; -const updateDataset = async ( - dbId: number, - datasetId: number, - sql: string, - columns: Array>, - owners: [number], - overrideColumns: boolean, -) => { - const endpoint = `api/v1/dataset/${datasetId}?override_columns=${overrideColumns}`; - const headers = { 'Content-Type': 'application/json' }; - const body = JSON.stringify({ - sql, - columns, - owners, - database_id: dbId, - }); - - const data: JsonResponse = await SupersetClient.put({ - endpoint, - headers, - body, - }); - return data.json.result; -}; - export default class ResultSet extends React.PureComponent< ResultSetProps, ResultSetState @@ -190,12 +123,6 @@ export default class ResultSet extends React.PureComponent< showExploreResultsButton: false, data: [], showSaveDatasetModal: false, - newSaveDatasetName: this.getDefaultDatasetName(), - saveDatasetRadioBtnState: DatasetRadioState.SAVE_NEW, - shouldOverwriteDataSet: false, - datasetToOverwrite: {}, - saveModalAutocompleteValue: '', - userDatasetOptions: [], alertIsOpen: false, }; this.changeSearch = this.changeSearch.bind(this); @@ -204,31 +131,11 @@ export default class ResultSet extends React.PureComponent< this.reFetchQueryResults = this.reFetchQueryResults.bind(this); this.toggleExploreResultsButton = this.toggleExploreResultsButton.bind(this); - this.handleSaveInDataset = this.handleSaveInDataset.bind(this); - this.handleHideSaveModal = this.handleHideSaveModal.bind(this); - this.handleDatasetNameChange = this.handleDatasetNameChange.bind(this); - this.handleSaveDatasetRadioBtnState = - this.handleSaveDatasetRadioBtnState.bind(this); - this.handleOverwriteCancel = this.handleOverwriteCancel.bind(this); - this.handleOverwriteDataset = this.handleOverwriteDataset.bind(this); - this.handleOverwriteDatasetOption = - this.handleOverwriteDatasetOption.bind(this); - this.handleSaveDatasetModalSearch = debounce( - this.handleSaveDatasetModalSearch.bind(this), - 1000, - ); - this.handleFilterAutocompleteOption = - this.handleFilterAutocompleteOption.bind(this); - this.handleOnChangeAutoComplete = - this.handleOnChangeAutoComplete.bind(this); - this.handleExploreBtnClick = this.handleExploreBtnClick.bind(this); } async componentDidMount() { // only do this the first time the component is rendered/mounted this.reRunQueryIfSessionTimeoutErrorOnMount(); - const userDatasetsOwned = await this.getUserDatasets(); - this.setState({ userDatasetOptions: userDatasetsOwned }); } UNSAFE_componentWillReceiveProps(nextProps: ResultSetProps) { @@ -260,185 +167,6 @@ export default class ResultSet extends React.PureComponent< } }; - getDefaultDatasetName = () => - `${this.props.query.tab} ${moment().format('MM/DD/YYYY HH:mm:ss')}`; - - handleOnChangeAutoComplete = () => { - this.setState({ datasetToOverwrite: {} }); - }; - - handleOverwriteDataset = async () => { - const { sql, results, dbId } = this.props.query; - const { datasetToOverwrite } = this.state; - - await updateDataset( - dbId, - datasetToOverwrite.datasetId, - sql, - results.selected_columns.map(d => ({ - column_name: d.name, - type: d.type, - is_dttm: d.is_dttm, - })), - datasetToOverwrite.owners.map((o: DatasetOwner) => o.id), - true, - ); - - this.setState({ - showSaveDatasetModal: false, - shouldOverwriteDataSet: false, - datasetToOverwrite: {}, - newSaveDatasetName: this.getDefaultDatasetName(), - }); - - exploreChart({ - ...EXPLORE_CHART_DEFAULT, - datasource: `${datasetToOverwrite.datasetId}__table`, - all_columns: results.selected_columns.map(d => d.name), - }); - }; - - handleSaveInDataset = () => { - // if user wants to overwrite a dataset we need to prompt them - if ( - this.state.saveDatasetRadioBtnState === - DatasetRadioState.OVERWRITE_DATASET - ) { - this.setState({ shouldOverwriteDataSet: true }); - return; - } - - const { schema, sql, dbId } = this.props.query; - let { templateParams } = this.props.query; - const selectedColumns = this.props.query?.results?.selected_columns || []; - - // The filters param is only used to test jinja templates. - // Remove the special filters entry from the templateParams - // before saving the dataset. - if (templateParams) { - const p = JSON.parse(templateParams); - /* eslint-disable-next-line no-underscore-dangle */ - if (p._filters) { - /* eslint-disable-next-line no-underscore-dangle */ - delete p._filters; - templateParams = JSON.stringify(p); - } - } - - this.props.actions - .createDatasource({ - schema, - sql, - dbId, - templateParams, - datasourceName: this.state.newSaveDatasetName, - columns: selectedColumns, - }) - .then((data: { table_id: number }) => { - exploreChart({ - datasource: `${data.table_id}__table`, - metrics: [], - groupby: [], - time_range: 'No filter', - viz_type: 'table', - all_columns: selectedColumns.map(c => c.name), - row_limit: 1000, - }); - }) - .catch(() => { - this.props.actions.addDangerToast( - t('An error occurred saving dataset'), - ); - }); - - this.setState({ - showSaveDatasetModal: false, - newSaveDatasetName: this.getDefaultDatasetName(), - }); - }; - - handleOverwriteDatasetOption = ( - _data: string, - option: Record, - ) => { - this.setState({ datasetToOverwrite: option }); - }; - - handleDatasetNameChange = (e: React.FormEvent) => { - // @ts-expect-error - this.setState({ newSaveDatasetName: e.target.value }); - }; - - handleHideSaveModal = () => { - this.setState({ - showSaveDatasetModal: false, - shouldOverwriteDataSet: false, - }); - }; - - handleSaveDatasetRadioBtnState = (e: RadioChangeEvent) => { - this.setState({ saveDatasetRadioBtnState: Number(e.target.value) }); - }; - - handleOverwriteCancel = () => { - this.setState({ shouldOverwriteDataSet: false, datasetToOverwrite: {} }); - }; - - handleExploreBtnClick = () => { - this.setState({ - showSaveDatasetModal: true, - }); - }; - - getUserDatasets = async (searchText = '') => { - // Making sure that autocomplete input has a value before rendering the dropdown - // Transforming the userDatasetsOwned data for SaveModalComponent) - const { userId } = this.props.user; - if (userId) { - const queryParams = rison.encode({ - filters: [ - { - col: 'table_name', - opr: 'ct', - value: searchText, - }, - { - col: 'owners', - opr: 'rel_m_m', - value: userId, - }, - ], - order_column: 'changed_on_delta_humanized', - order_direction: 'desc', - }); - - const response = await makeApi({ - method: 'GET', - endpoint: '/api/v1/dataset', - })(`q=${queryParams}`); - - return response.result.map( - (r: { table_name: string; id: number; owners: [DatasetOwner] }) => ({ - value: r.table_name, - datasetId: r.id, - owners: r.owners, - }), - ); - } - - return null; - }; - - handleSaveDatasetModalSearch = async (searchText: string) => { - const userDatasetsOwned = await this.getUserDatasets(searchText); - this.setState({ userDatasetOptions: userDatasetsOwned }); - }; - - handleFilterAutocompleteOption = ( - inputValue: string, - option: { value: string; datasetId: number }, - ) => option.value.toLowerCase().includes(inputValue.toLowerCase()); - clearQueryResults(query: Query) { this.props.actions.clearQueryResults(query); } @@ -490,53 +218,28 @@ export default class ResultSet extends React.PureComponent< } const { columns } = this.props.query.results; // Added compute logic to stop user from being able to Save & Explore - const { - saveDatasetRadioBtnState, - newSaveDatasetName, - datasetToOverwrite, - saveModalAutocompleteValue, - shouldOverwriteDataSet, - userDatasetOptions, - showSaveDatasetModal, - } = this.state; - const disableSaveAndExploreBtn = - (saveDatasetRadioBtnState === DatasetRadioState.SAVE_NEW && - newSaveDatasetName.length === 0) || - (saveDatasetRadioBtnState === DatasetRadioState.OVERWRITE_DATASET && - Object.keys(datasetToOverwrite).length === 0 && - saveModalAutocompleteValue.length === 0); + const { showSaveDatasetModal } = this.state; return ( this.setState({ showSaveDatasetModal: false })} buttonTextOnSave={t('Save & Explore')} buttonTextOnOverwrite={t('Overwrite & Explore')} modalDescription={t( 'Save this query as a virtual dataset to continue exploring', )} + user={this.props.user} + query={this.props.query} + actions={this.props.actions} /> {this.props.visualize && this.props.database?.allows_virtual_table_explore && ( this.setState({ showSaveDatasetModal: true })} /> )} {this.props.csv && ( @@ -579,10 +282,6 @@ export default class ResultSet extends React.PureComponent< return
; } - onAlertClose = () => { - this.setState({ alertIsOpen: false }); - }; - renderRowsReturned() { const { results, rows, queryLimit, limitingFactor } = this.props.query; let limitMessage; @@ -648,7 +347,7 @@ export default class ResultSet extends React.PureComponent< this.setState({ alertIsOpen: false })} description={t( 'The number of rows displayed is limited to %s by the dropdown.', rows, @@ -660,7 +359,7 @@ export default class ResultSet extends React.PureComponent<
this.setState({ alertIsOpen: false })} message={t('%(rows)d rows returned', { rows: rowsCount })} description={ isAdmin @@ -683,9 +382,7 @@ export default class ResultSet extends React.PureComponent< exploreDBId = this.props.database.explore_database_id; } - if (this.props.showSql) { - sql = ; - } + if (this.props.showSql) sql = ; if (query.state === 'stopped') { return ; diff --git a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx index 305402eb85786..b0de70b24bc15 100644 --- a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx +++ b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx @@ -17,41 +17,43 @@ * under the License. */ -import React, { FunctionComponent } from 'react'; -import { AutoCompleteProps } from 'antd/lib/auto-complete'; +import React, { FunctionComponent, useState } from 'react'; import { Radio } from 'src/components/Radio'; import { AutoComplete, RadioChangeEvent } from 'src/components'; import { Input } from 'src/components/Input'; import StyledModal from 'src/components/Modal'; import Button from 'src/components/Button'; -import { styled, t } from '@superset-ui/core'; +import { + styled, + t, + SupersetClient, + makeApi, + JsonResponse, +} from '@superset-ui/core'; +import moment from 'moment'; +import rison from 'rison'; +import { + DatasetRadioState, + EXPLORE_CHART_DEFAULT, + DatasetOwner, + DatasetOptionAutocomplete, + Query, +} from 'src/SqlLab/types'; +import { DatasourceMeta } from '@superset-ui/chart-controls'; +import { exploreChart } from 'src/explore/exploreUtils'; interface SaveDatasetModalProps { visible: boolean; - onOk: () => void; onHide: () => void; - handleDatasetNameChange: (e: React.FormEvent) => void; - handleSaveDatasetModalSearch: (searchText: string) => Promise; - filterAutocompleteOption: ( - inputValue: string, - option: { value: string; datasetId: number }, - ) => boolean; - handleSaveDatasetRadioBtnState: (e: RadioChangeEvent) => void; - handleOverwriteCancel: () => void; - handleOverwriteDataset: () => void; - handleOverwriteDatasetOption: ( - data: string, - option: Record, - ) => void; - onChangeAutoComplete: () => void; - defaultCreateDatasetValue: string; - disableSaveAndExploreBtn: boolean; - saveDatasetRadioBtnState: number; - shouldOverwriteDataset: boolean; - userDatasetOptions: AutoCompleteProps['options']; buttonTextOnSave: string; buttonTextOnOverwrite: string; modalDescription?: string; + datasource?: DatasourceMeta; + user: { + userId: number; + }; + query?: Query; + actions?: Record; } const Styles = styled.div` @@ -77,99 +79,290 @@ const Styles = styled.div` } `; +const updateDataset = async ( + dbId: number, + datasetId: number, + sql: string, + columns: Array>, + owners: [number], + overrideColumns: boolean, +) => { + const endpoint = `api/v1/dataset/${datasetId}?override_columns=${overrideColumns}`; + const headers = { 'Content-Type': 'application/json' }; + const body = JSON.stringify({ + sql, + columns, + owners, + database_id: dbId, + }); + + const data: JsonResponse = await SupersetClient.put({ + endpoint, + headers, + body, + }); + return data.json.result; +}; + // eslint-disable-next-line no-empty-pattern export const SaveDatasetModal: FunctionComponent = ({ visible, - onOk, onHide, - handleDatasetNameChange, - handleSaveDatasetRadioBtnState, - saveDatasetRadioBtnState, - shouldOverwriteDataset, - handleOverwriteCancel, - handleOverwriteDataset, - handleOverwriteDatasetOption, - defaultCreateDatasetValue, - disableSaveAndExploreBtn, - handleSaveDatasetModalSearch, - filterAutocompleteOption, - userDatasetOptions, - onChangeAutoComplete, buttonTextOnSave, buttonTextOnOverwrite, modalDescription, -}) => ( - - {!shouldOverwriteDataset && ( - - )} - {shouldOverwriteDataset && ( - <> - + datasource, + user, + query, + actions, +}) => { + const getDefaultDatasetName = () => + `${query?.tab || datasource?.sl_dataset?.query.tab} ${moment().format( + 'MM/DD/YYYY HH:mm:ss', + )}`; + + const [newSaveDatasetName, setNewSaveDatasetName] = useState( + getDefaultDatasetName(), + ); + const [saveDatasetRadioBtnState, setSaveDatasetRadioBtnState] = useState( + DatasetRadioState.SAVE_NEW, + ); + const [shouldOverwriteDataset, setShouldOverwriteDataset] = useState(false); + const [userDatasetOptions, setUserDatasetOptions] = useState< + DatasetOptionAutocomplete[] + >([]); + const [datasetToOverwrite, setDatasetToOverwrite] = useState< + Record + >({}); + const [saveModalAutocompleteValue] = useState(''); + + const handleOverwriteDataset = async () => { + await updateDataset( + query!.dbId, + datasetToOverwrite.datasetId, + query!.sql, + // TODO: lyndsiWilliams - Define d + query!.results.selected_columns.map((d: any) => ({ + column_name: d.name, + type: d.type, + is_dttm: d.is_dttm, + })), + datasetToOverwrite.owners.map((o: DatasetOwner) => o.id), + true, + ); + + setShouldOverwriteDataset(false); + setDatasetToOverwrite({}); + setNewSaveDatasetName(getDefaultDatasetName()); + + exploreChart({ + ...EXPLORE_CHART_DEFAULT, + datasource: `${datasetToOverwrite.datasetId}__table`, + // TODO: lyndsiWilliams - Define d + all_columns: query!.results.selected_columns.map((d: any) => d.name), + }); + }; + + const getUserDatasets = async (searchText = '') => { + // Making sure that autocomplete input has a value before rendering the dropdown + // Transforming the userDatasetsOwned data for SaveModalComponent) + const { userId } = user; + if (userId) { + const queryParams = rison.encode({ + filters: [ + { + col: 'table_name', + opr: 'ct', + value: searchText, + }, + { + col: 'owners', + opr: 'rel_m_m', + value: userId, + }, + ], + order_column: 'changed_on_delta_humanized', + order_direction: 'desc', + }); + + const response = await makeApi({ + method: 'GET', + endpoint: '/api/v1/dataset', + })(`q=${queryParams}`); + + return response.result.map( + (r: { table_name: string; id: number; owners: [DatasetOwner] }) => ({ + value: r.table_name, + datasetId: r.id, + owners: r.owners, + }), + ); + } + + return null; + }; + + const handleSaveInDataset = () => { + // if user wants to overwrite a dataset we need to prompt them + if (saveDatasetRadioBtnState === DatasetRadioState.OVERWRITE_DATASET) { + setShouldOverwriteDataset(true); + return; + } + + const selectedColumns = query?.results?.selected_columns || []; + + // The filters param is only used to test jinja templates. + // Remove the special filters entry from the templateParams + // before saving the dataset. + if (query?.templateParams) { + const p = JSON.parse(query?.templateParams); + /* eslint-disable-next-line no-underscore-dangle */ + if (p._filters) { + /* eslint-disable-next-line no-underscore-dangle */ + delete p._filters; + // eslint-disable-next-line no-param-reassign + query.templateParams = JSON.stringify(p); + } + } + + // TODO: lyndsiWilliams - set up when the back end logic is implemented + actions + ?.createDatasource({ + schema: query?.schema, + sql: query?.sql, + dbId: query?.dbId, + templateParams: query?.templateParams, + datasourceName: newSaveDatasetName, + columns: selectedColumns, + }) + .then((data: { table_id: number }) => { + exploreChart({ + datasource: `${data.table_id}__table`, + metrics: [], + groupby: [], + time_range: 'No filter', + viz_type: 'table', + all_columns: selectedColumns.map(c => c.name), + row_limit: 1000, + }); + }) + .catch(() => { + actions.addDangerToast(t('An error occurred saving dataset')); + }); + + setNewSaveDatasetName(getDefaultDatasetName()); + onHide(); + }; + + const handleSaveDatasetModalSearch = async (searchText: string) => { + const userDatasetsOwned = await getUserDatasets(searchText); + setUserDatasetOptions(userDatasetsOwned); + }; + + const handleOverwriteDatasetOption = ( + _data: string, + option: Record, + ) => setDatasetToOverwrite(option); + + const handleDatasetNameChange = (e: React.FormEvent) => { + // @ts-expect-error + setNewSaveDatasetName(e.target.value); + }; + + const handleSaveDatasetRadioBtnState = (e: RadioChangeEvent) => { + setSaveDatasetRadioBtnState(Number(e.target.value)); + }; + + const handleOverwriteCancel = () => { + setShouldOverwriteDataset(false); + setDatasetToOverwrite({}); + }; + + const disableSaveAndExploreBtn = + (saveDatasetRadioBtnState === DatasetRadioState.SAVE_NEW && + newSaveDatasetName.length === 0) || + (saveDatasetRadioBtnState === DatasetRadioState.OVERWRITE_DATASET && + Object.keys(datasetToOverwrite).length === 0 && + saveModalAutocompleteValue.length === 0); + + const filterAutocompleteOption = ( + inputValue: string, + option: { value: string; datasetId: number }, + ) => option.value.toLowerCase().includes(inputValue.toLowerCase()); + + return ( + + {!shouldOverwriteDataset && ( - - )} - - } - > - - {!shouldOverwriteDataset && ( -
- {modalDescription && ( -
{modalDescription}
)} - - - {t('Save as new')} - - - - {t('Overwrite existing')} - - - -
- )} - {shouldOverwriteDataset && ( -
- {t('Are you sure you want to overwrite this dataset?')} -
- )} -
-
-); + {shouldOverwriteDataset && ( + <> + + + + )} + + } + > + + {!shouldOverwriteDataset && ( +
+ {modalDescription && ( +
{modalDescription}
+ )} + + + {t('Save as new')} + + + + {t('Overwrite existing')} + setDatasetToOverwrite({})} + placeholder={t('Select or type dataset name')} + filterOption={filterAutocompleteOption} + disabled={saveDatasetRadioBtnState !== 2} + /> + + +
+ )} + {shouldOverwriteDataset && ( +
+ {t('Are you sure you want to overwrite this dataset?')} +
+ )} +
+
+ ); +}; diff --git a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx index cbbfc8dfc6aa8..4e7c4b8cd81c0 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx @@ -17,15 +17,7 @@ * under the License. */ import React, { useEffect, useMemo, useRef, useState } from 'react'; -import { - css, - styled, - t, - makeApi, - SupersetClient, - JsonResponse, - DatasourceType, -} from '@superset-ui/core'; +import { css, styled, t, DatasourceType } from '@superset-ui/core'; import { ControlConfig, DatasourceMeta, @@ -36,22 +28,12 @@ import { matchSorter, rankings } from 'match-sorter'; import Collapse from 'src/components/Collapse'; import Alert from 'src/components/Alert'; import { SaveDatasetModal } from 'src/SqlLab/components/SaveDatasetModal'; -import { exploreChart } from 'src/explore/exploreUtils'; -import moment from 'moment'; -import rison from 'rison'; -import { RadioChangeEvent } from 'src/components'; import { Input } from 'src/components/Input'; import { FAST_DEBOUNCE } from 'src/constants'; import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags'; import { ExploreActions } from 'src/explore/actions/exploreActions'; import Control from 'src/explore/components/Control'; import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes'; -import { - DatasetRadioState, - EXPLORE_CHART_DEFAULT, - DatasetOwner, - DatasetOptionAutocomplete, -} from 'src/SqlLab/types'; import DatasourcePanelDragOption from './DatasourcePanelDragOption'; import { DndItemType } from '../DndItemType'; import { StyledColumnOption, StyledMetricOption } from '../optionRenderers'; @@ -187,31 +169,6 @@ const StyledInfoboxWrapper = styled.div` `} `; -const updateDataset = async ( - dbId: number, - datasetId: number, - sql: string, - columns: Array>, - owners: [number], - overrideColumns: boolean, -) => { - const endpoint = `api/v1/dataset/${datasetId}?override_columns=${overrideColumns}`; - const headers = { 'Content-Type': 'application/json' }; - const body = JSON.stringify({ - sql, - columns, - owners, - database_id: dbId, - }); - - const data: JsonResponse = await SupersetClient.put({ - endpoint, - headers, - body, - }); - return data.json.result; -}; - const LabelContainer = (props: { children: React.ReactElement; className: string; @@ -251,26 +208,7 @@ export default function DataSourcePanel({ [_columns], ); - const getDefaultDatasetName = () => - `${datasource?.sl_dataset?.query.tab} ${moment().format( - 'MM/DD/YYYY HH:mm:ss', - )}`; - const [showSaveDatasetModal, setShowSaveDatasetModal] = useState(false); - const [newSaveDatasetName, setNewSaveDatasetName] = useState( - getDefaultDatasetName(), - ); - const [saveDatasetRadioBtnState, setSaveDatasetRadioBtnState] = useState( - DatasetRadioState.SAVE_NEW, - ); - const [shouldOverwriteDataSet, setShouldOverwriteDataSet] = useState(false); - const [userDatasetOptions, setUserDatasetOptions] = useState< - DatasetOptionAutocomplete[] - >([]); - const [datasetToOverwrite, setDatasetToOverwrite] = useState< - Record - >({}); - const [saveModalAutocompleteValue] = useState(''); const [inputValue, setInputValue] = useState(''); const [lists, setList] = useState({ columns, @@ -369,170 +307,6 @@ export default function DataSourcePanel({ [lists.columns, showAllColumns], ); - const handleOverwriteDataset = async () => { - const { sql, results, dbId } = datasource?.sl_dataset?.query; - - await updateDataset( - dbId, - datasetToOverwrite.datasetId, - sql, - // TODO: lyndsiWilliams - Define d - results.selected_columns.map((d: any) => ({ - column_name: d.name, - type: d.type, - is_dttm: d.is_dttm, - })), - datasetToOverwrite.owners.map((o: DatasetOwner) => o.id), - true, - ); - - setShowSaveDatasetModal(false); - setShouldOverwriteDataSet(false); - setDatasetToOverwrite({}); - setNewSaveDatasetName(getDefaultDatasetName()); - - exploreChart({ - ...EXPLORE_CHART_DEFAULT, - datasource: `${datasetToOverwrite.datasetId}__table`, - // TODO: lyndsiWilliams - Define d - all_columns: results.selected_columns.map((d: any) => d.name), - }); - }; - - const getUserDatasets = async (searchText = '') => { - // Making sure that autocomplete input has a value before rendering the dropdown - // Transforming the userDatasetsOwned data for SaveModalComponent) - const { userId } = user; - if (userId) { - const queryParams = rison.encode({ - filters: [ - { - col: 'table_name', - opr: 'ct', - value: searchText, - }, - { - col: 'owners', - opr: 'rel_m_m', - value: userId, - }, - ], - order_column: 'changed_on_delta_humanized', - order_direction: 'desc', - }); - - const response = await makeApi({ - method: 'GET', - endpoint: '/api/v1/dataset', - })(`q=${queryParams}`); - - return response.result.map( - (r: { table_name: string; id: number; owners: [DatasetOwner] }) => ({ - value: r.table_name, - datasetId: r.id, - owners: r.owners, - }), - ); - } - - return null; - }; - - const handleSaveDatasetModalSearch = async (searchText: string) => { - const userDatasetsOwned = await getUserDatasets(searchText); - setUserDatasetOptions(userDatasetsOwned); - }; - - const handleSaveInDataset = () => { - // if user wants to overwrite a dataset we need to prompt them - if (saveDatasetRadioBtnState === DatasetRadioState.OVERWRITE_DATASET) { - setShouldOverwriteDataSet(true); - return; - } - - // TODO: lyndsiWilliams - set up when the back end logic is implemented - // const { schema, sql, dbId } = datasource.sl_dataset.query; - let { templateParams } = datasource?.sl_dataset?.query; - // const selectedColumns = - // datasource.sl_dataset.query?.results?.selected_columns || []; - - // The filters param is only used to test jinja templates. - // Remove the special filters entry from the templateParams - // before saving the dataset. - if (templateParams) { - const p = JSON.parse(templateParams); - /* eslint-disable-next-line no-underscore-dangle */ - if (p._filters) { - /* eslint-disable-next-line no-underscore-dangle */ - delete p._filters; - templateParams = JSON.stringify(p); - } - } - - // TODO: lyndsiWilliams - set up when the back end logic is implemented - // createDatasource({ - // schema, - // sql, - // dbId, - // templateParams, - // datasourceName: newSaveDatasetName, - // columns: selectedColumns, - // }); - // .then((data: { table_id: number }) => { - // exploreChart({ - // datasource: `${data.table_id}__table`, - // metrics: [], - // groupby: [], - // time_range: 'No filter', - // viz_type: 'table', - // all_columns: selectedColumns.map((c) => c.name), - // row_limit: 1000, - // }); - // }) - // .catch(() => { - // actions.addDangerToast(t('An error occurred saving dataset')); - // }); - - setShowSaveDatasetModal(false); - setNewSaveDatasetName(getDefaultDatasetName()); - }; - - const handleOverwriteDatasetOption = ( - _data: string, - option: Record, - ) => setDatasetToOverwrite(option); - - const handleDatasetNameChange = (e: React.FormEvent) => { - // @ts-expect-error - setNewSaveDatasetName(e.target.value); - }; - - const handleHideSaveModal = () => { - setShowSaveDatasetModal(false); - setShouldOverwriteDataSet(false); - }; - - const handleSaveDatasetRadioBtnState = (e: RadioChangeEvent) => { - setSaveDatasetRadioBtnState(Number(e.target.value)); - }; - - const handleOverwriteCancel = () => { - setShouldOverwriteDataSet(false); - setDatasetToOverwrite({}); - }; - - const disableSaveAndExploreBtn = - (saveDatasetRadioBtnState === DatasetRadioState.SAVE_NEW && - newSaveDatasetName.length === 0) || - (saveDatasetRadioBtnState === DatasetRadioState.OVERWRITE_DATASET && - Object.keys(datasetToOverwrite).length === 0 && - saveModalAutocompleteValue.length === 0); - - const handleFilterAutocompleteOption = ( - inputValue: string, - option: { value: string; datasetId: number }, - ) => option.value.toLowerCase().includes(inputValue.toLowerCase()); - const showInfoboxCheck = () => { if (sessionStorage.getItem('showInfobox') === 'false') return false; return true; @@ -678,23 +452,10 @@ export default function DataSourcePanel({ setDatasetToOverwrite({})} + onHide={() => setShowSaveDatasetModal(false)} buttonTextOnSave={t('Save')} buttonTextOnOverwrite={t('Overwrite')} + user={user} /> {datasource.id != null && mainBody} From fe7085a7a770483161512b139b74167d38858cbf Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Wed, 4 May 2022 13:04:39 -0500 Subject: [PATCH 12/57] Change DatasourceMeta type to Dataset --- .../packages/superset-ui-chart-controls/src/types.ts | 11 +++-------- .../src/utils/columnChoices.ts | 4 ++-- .../src/SqlLab/components/SaveDatasetModal/index.tsx | 9 +++------ .../dashboard/components/FiltersBadge/selectors.ts | 4 ++-- .../FiltersConfigModal/FiltersConfigForm/utils.ts | 8 ++++---- superset-frontend/src/dashboard/types.ts | 4 ++-- .../src/explore/actions/exploreActions.ts | 6 +++--- .../src/explore/components/ControlPanelsContainer.tsx | 8 ++++---- .../src/explore/components/DatasourcePanel/index.tsx | 8 +++----- .../getControlValuesCompatibleWithDatasource.ts | 10 +++------- .../src/explore/reducers/getInitialState.ts | 7 ++----- superset-frontend/src/explore/types.ts | 4 ++-- 12 files changed, 33 insertions(+), 50 deletions(-) diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/types.ts b/superset-frontend/packages/superset-ui-chart-controls/src/types.ts index 5b39748dbd972..662bc9d0b3e38 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/types.ts +++ b/superset-frontend/packages/superset-ui-chart-controls/src/types.ts @@ -51,7 +51,7 @@ export type ColumnMeta = Omit & { id?: number; } & AnyDict; -export interface DatasourceMeta { +export interface Dataset { id: number; type: DatasourceType; columns: ColumnMeta[]; @@ -65,16 +65,11 @@ export interface DatasourceMeta { granularity_sqla?: string; datasource_name: string | null; description: string | null; - sl_dataset?: { - sl_table?: any; - query?: any; - saved_query?: any; - }; } export interface ControlPanelState { form_data: QueryFormData; - datasource: DatasourceMeta | null; + datasource: Dataset | null; controls: ControlStateMapping; } @@ -93,7 +88,7 @@ export interface ActionDispatcher< * Mapping of action dispatchers */ export interface ControlPanelActionDispatchers { - setDatasource: ActionDispatcher<[DatasourceMeta]>; + setDatasource: ActionDispatcher<[Dataset]>; } /** diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/utils/columnChoices.ts b/superset-frontend/packages/superset-ui-chart-controls/src/utils/columnChoices.ts index 3725f175e7ffc..7977e9dc7ba86 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/utils/columnChoices.ts +++ b/superset-frontend/packages/superset-ui-chart-controls/src/utils/columnChoices.ts @@ -16,13 +16,13 @@ * specific language governing permissions and limitations * under the License. */ -import { DatasourceMeta } from '../types'; +import { Dataset } from '../types'; /** * Convert Datasource columns to column choices */ export default function columnChoices( - datasource?: DatasourceMeta | null, + datasource?: Dataset | null, ): [string, string][] { return ( datasource?.columns diff --git a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx index b0de70b24bc15..3cd05407f2239 100644 --- a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx +++ b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx @@ -39,7 +39,7 @@ import { DatasetOptionAutocomplete, Query, } from 'src/SqlLab/types'; -import { DatasourceMeta } from '@superset-ui/chart-controls'; +import { Dataset } from '@superset-ui/chart-controls'; import { exploreChart } from 'src/explore/exploreUtils'; interface SaveDatasetModalProps { @@ -48,7 +48,7 @@ interface SaveDatasetModalProps { buttonTextOnSave: string; buttonTextOnOverwrite: string; modalDescription?: string; - datasource?: DatasourceMeta; + datasource?: Dataset; user: { userId: number; }; @@ -111,15 +111,12 @@ export const SaveDatasetModal: FunctionComponent = ({ buttonTextOnSave, buttonTextOnOverwrite, modalDescription, - datasource, user, query, actions, }) => { const getDefaultDatasetName = () => - `${query?.tab || datasource?.sl_dataset?.query.tab} ${moment().format( - 'MM/DD/YYYY HH:mm:ss', - )}`; + `${query?.tab} ${moment().format('MM/DD/YYYY HH:mm:ss')}`; const [newSaveDatasetName, setNewSaveDatasetName] = useState( getDefaultDatasetName(), diff --git a/superset-frontend/src/dashboard/components/FiltersBadge/selectors.ts b/superset-frontend/src/dashboard/components/FiltersBadge/selectors.ts index 0b84c73411db2..6d9ab6fe9c9d6 100644 --- a/superset-frontend/src/dashboard/components/FiltersBadge/selectors.ts +++ b/superset-frontend/src/dashboard/components/FiltersBadge/selectors.ts @@ -42,8 +42,8 @@ export enum IndicatorStatus { const TIME_GRANULARITY_FIELDS = new Set(Object.values(TIME_FILTER_MAP)); -// As of 2020-09-28, the DatasourceMeta type in superset-ui is incorrect. -// Should patch it here until the DatasourceMeta type is updated. +// As of 2020-09-28, the Dataset type in superset-ui is incorrect. +// Should patch it here until the Dataset type is updated. type Datasource = { time_grain_sqla?: [string, string][]; granularity?: [string, string][]; diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/utils.ts b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/utils.ts index e946cb671405f..8fdb3b0325997 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/utils.ts +++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/utils.ts @@ -19,7 +19,7 @@ import { flatMapDeep } from 'lodash'; import { FormInstance } from 'src/components'; import React from 'react'; -import { CustomControlItem, DatasourceMeta } from '@superset-ui/chart-controls'; +import { CustomControlItem, Dataset } from '@superset-ui/chart-controls'; import { Column, ensureIsArray, GenericDataType } from '@superset-ui/core'; import { DatasourcesState, ChartsState } from 'src/dashboard/types'; @@ -80,16 +80,16 @@ type DatasetSelectValue = { }; export const datasetToSelectOption = ( - item: DatasourceMeta & { table_name: string }, + item: Dataset & { table_name: string }, ): DatasetSelectValue => ({ value: item.id, label: item.table_name, }); -// TODO: add column_types field to DatasourceMeta +// TODO: add column_types field to Dataset // We return true if column_types is undefined or empty as a precaution against backend failing to return column_types export const hasTemporalColumns = ( - dataset: DatasourceMeta & { column_types: GenericDataType[] }, + dataset: Dataset & { column_types: GenericDataType[] }, ) => { const columnTypes = ensureIsArray(dataset?.column_types); return ( diff --git a/superset-frontend/src/dashboard/types.ts b/superset-frontend/src/dashboard/types.ts index c0b312d434423..e4b8227689ce4 100644 --- a/superset-frontend/src/dashboard/types.ts +++ b/superset-frontend/src/dashboard/types.ts @@ -24,7 +24,7 @@ import { JsonObject, NativeFiltersState, } from '@superset-ui/core'; -import { DatasourceMeta } from '@superset-ui/chart-controls'; +import { Dataset } from '@superset-ui/chart-controls'; import { chart } from 'src/components/Chart/chartReducer'; import componentTypes from 'src/dashboard/util/componentTypes'; @@ -84,7 +84,7 @@ export type DashboardInfo = { export type ChartsState = { [key: string]: Chart }; -export type Datasource = DatasourceMeta & { +export type Datasource = Dataset & { uid: string; column_types: GenericDataType[]; table_name: string; diff --git a/superset-frontend/src/explore/actions/exploreActions.ts b/superset-frontend/src/explore/actions/exploreActions.ts index 8e73b32a9cd63..cf6bb3f7f4ab2 100644 --- a/superset-frontend/src/explore/actions/exploreActions.ts +++ b/superset-frontend/src/explore/actions/exploreActions.ts @@ -17,7 +17,7 @@ * under the License. */ /* eslint camelcase: 0 */ -import { DatasourceMeta } from '@superset-ui/chart-controls'; +import { Dataset } from '@superset-ui/chart-controls'; import { t, SupersetClient, @@ -39,12 +39,12 @@ export function setDatasourceType(datasourceType: DatasourceType) { } export const SET_DATASOURCE = 'SET_DATASOURCE'; -export function setDatasource(datasource: DatasourceMeta) { +export function setDatasource(datasource: Dataset) { return { type: SET_DATASOURCE, datasource }; } export const SET_DATASOURCES = 'SET_DATASOURCES'; -export function setDatasources(datasources: DatasourceMeta[]) { +export function setDatasources(datasources: Dataset[]) { return { type: SET_DATASOURCES, datasources }; } diff --git a/superset-frontend/src/explore/components/ControlPanelsContainer.tsx b/superset-frontend/src/explore/components/ControlPanelsContainer.tsx index ef9517e4ddf87..6ed73f2b3869e 100644 --- a/superset-frontend/src/explore/components/ControlPanelsContainer.tsx +++ b/superset-frontend/src/explore/components/ControlPanelsContainer.tsx @@ -40,7 +40,7 @@ import { ControlPanelSectionConfig, ControlState, CustomControlItem, - DatasourceMeta, + Dataset, ExpandedControlItem, InfoTooltipWithTrigger, sections, @@ -174,13 +174,13 @@ const isTimeSection = (section: ControlPanelSectionConfig): boolean => (sections.legacyRegularTime.label === section.label || sections.legacyTimeseriesTime.label === section.label); -const hasTimeColumn = (datasource: DatasourceMeta): boolean => +const hasTimeColumn = (datasource: Dataset): boolean => datasource?.columns?.some(c => c.is_dttm) || datasource.type === DatasourceType.Druid; const sectionsToExpand = ( sections: ControlPanelSectionConfig[], - datasource: DatasourceMeta, + datasource: Dataset, ): string[] => // avoid expanding time section if datasource doesn't include time column sections.reduce( @@ -193,7 +193,7 @@ const sectionsToExpand = ( function getState( vizType: string, - datasource: DatasourceMeta, + datasource: Dataset, datasourceType: DatasourceType, ) { const querySections: ControlPanelSectionConfig[] = []; diff --git a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx index 4e7c4b8cd81c0..67e5f172cd2ef 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx @@ -20,7 +20,7 @@ import React, { useEffect, useMemo, useRef, useState } from 'react'; import { css, styled, t, DatasourceType } from '@superset-ui/core'; import { ControlConfig, - DatasourceMeta, + Dataset, ColumnMeta, } from '@superset-ui/chart-controls'; import { debounce } from 'lodash'; @@ -33,18 +33,16 @@ import { FAST_DEBOUNCE } from 'src/constants'; import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags'; import { ExploreActions } from 'src/explore/actions/exploreActions'; import Control from 'src/explore/components/Control'; -import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes'; import DatasourcePanelDragOption from './DatasourcePanelDragOption'; import { DndItemType } from '../DndItemType'; import { StyledColumnOption, StyledMetricOption } from '../optionRenderers'; interface DatasourceControl extends ControlConfig { - datasource?: DatasourceMeta; - user: UserWithPermissionsAndRoles; + datasource?: Dataset; } export interface Props { - datasource: DatasourceMeta; + datasource: Dataset; controls: { datasource: DatasourceControl; }; diff --git a/superset-frontend/src/explore/controlUtils/getControlValuesCompatibleWithDatasource.ts b/superset-frontend/src/explore/controlUtils/getControlValuesCompatibleWithDatasource.ts index efd594cb7ff86..e070e82464d52 100644 --- a/superset-frontend/src/explore/controlUtils/getControlValuesCompatibleWithDatasource.ts +++ b/superset-frontend/src/explore/controlUtils/getControlValuesCompatibleWithDatasource.ts @@ -17,11 +17,7 @@ * under the License. */ -import { - ControlState, - DatasourceMeta, - Metric, -} from '@superset-ui/chart-controls'; +import { ControlState, Dataset, Metric } from '@superset-ui/chart-controls'; import { Column, isAdhocMetricSimple, @@ -33,7 +29,7 @@ import { import AdhocMetric from 'src/explore/components/controls/MetricControl/AdhocMetric'; const isControlValueCompatibleWithDatasource = ( - datasource: DatasourceMeta, + datasource: Dataset, controlState: ControlState, value: any, ) => { @@ -78,7 +74,7 @@ const isControlValueCompatibleWithDatasource = ( }; export const getControlValuesCompatibleWithDatasource = ( - datasource: DatasourceMeta, + datasource: Dataset, controlState: ControlState, value: JsonValue, ) => { diff --git a/superset-frontend/src/explore/reducers/getInitialState.ts b/superset-frontend/src/explore/reducers/getInitialState.ts index 659e834d2faad..50e1211a21276 100644 --- a/superset-frontend/src/explore/reducers/getInitialState.ts +++ b/superset-frontend/src/explore/reducers/getInitialState.ts @@ -18,10 +18,7 @@ */ import shortid from 'shortid'; import { DatasourceType, JsonObject, QueryFormData } from '@superset-ui/core'; -import { - ControlStateMapping, - DatasourceMeta, -} from '@superset-ui/chart-controls'; +import { ControlStateMapping, Dataset } from '@superset-ui/chart-controls'; import { CommonBootstrapData, UserWithPermissionsAndRoles, @@ -42,7 +39,7 @@ export interface ExplorePageBootstrapData extends JsonObject { can_download: boolean; can_overwrite: boolean; common: CommonBootstrapData; - datasource: DatasourceMeta; + datasource: Dataset; datasource_id: number; datasource_type: DatasourceType; forced_height: string | null; diff --git a/superset-frontend/src/explore/types.ts b/superset-frontend/src/explore/types.ts index fe6436ab86d0e..50ef57b9bb40b 100644 --- a/superset-frontend/src/explore/types.ts +++ b/superset-frontend/src/explore/types.ts @@ -22,7 +22,7 @@ import { AnnotationData, AdhocMetric, } from '@superset-ui/core'; -import { ColumnMeta, DatasourceMeta } from '@superset-ui/chart-controls'; +import { ColumnMeta, Dataset } from '@superset-ui/chart-controls'; import { DatabaseObject } from 'src/views/CRUD/types'; export { Slice, Chart } from 'src/types/Chart'; @@ -56,7 +56,7 @@ export type OptionSortType = Partial< ColumnMeta & AdhocMetric & { saved_metric_name: string } >; -export type Datasource = DatasourceMeta & { +export type Datasource = Dataset & { database?: DatabaseObject; datasource?: string; schema?: string; From 29f74e2efc1396919c7c712c31a0229201c50241 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Wed, 4 May 2022 13:06:41 -0500 Subject: [PATCH 13/57] Add ExploreDatasource union type to save dataset modal --- .../src/SqlLab/components/SaveDatasetModal/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx index 3cd05407f2239..81c7be49ee809 100644 --- a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx +++ b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx @@ -42,13 +42,15 @@ import { import { Dataset } from '@superset-ui/chart-controls'; import { exploreChart } from 'src/explore/exploreUtils'; +type ExploreDatasource = Dataset | Query; + interface SaveDatasetModalProps { visible: boolean; onHide: () => void; buttonTextOnSave: string; buttonTextOnOverwrite: string; modalDescription?: string; - datasource?: Dataset; + datasource?: ExploreDatasource; user: { userId: number; }; From 7aeba6b599d189cb8b88c189285a9f62e8e68296 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Wed, 4 May 2022 13:25:34 -0500 Subject: [PATCH 14/57] Get user info from redux inside save dataset modal --- .../SqlLab/components/QueryTable/index.tsx | 4 +-- .../src/SqlLab/components/ResultSet/index.tsx | 1 - .../components/SaveDatasetModal/index.tsx | 12 +++++--- superset-frontend/src/SqlLab/types.ts | 28 ++++++++++++++++++- .../components/DatasourcePanel/index.tsx | 5 ---- 5 files changed, 37 insertions(+), 13 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/QueryTable/index.tsx b/superset-frontend/src/SqlLab/components/QueryTable/index.tsx index 90d2219497583..b860feb89af07 100644 --- a/superset-frontend/src/SqlLab/components/QueryTable/index.tsx +++ b/superset-frontend/src/SqlLab/components/QueryTable/index.tsx @@ -28,7 +28,7 @@ import Button from 'src/components/Button'; import { fDuration } from 'src/utils/dates'; import Icons from 'src/components/Icons'; import { Tooltip } from 'src/components/Tooltip'; -import { Query, RootState } from 'src/SqlLab/types'; +import { Query, SqlLabRootState } from 'src/SqlLab/types'; import ModalTrigger from 'src/components/ModalTrigger'; import { UserWithPermissionsAndRoles as User } from 'src/types/bootstrapTypes'; import ResultSet from '../ResultSet'; @@ -91,7 +91,7 @@ const QueryTable = ({ [columns], ); - const user = useSelector(state => state.sqlLab.user); + const user = useSelector(state => state.sqlLab.user); const { queryEditorSetAndSaveSql, diff --git a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx index e10a42e3c52ea..226f28b18bf8b 100644 --- a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx +++ b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx @@ -230,7 +230,6 @@ export default class ResultSet extends React.PureComponent< modalDescription={t( 'Save this query as a virtual dataset to continue exploring', )} - user={this.props.user} query={this.props.query} actions={this.props.actions} /> diff --git a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx index 81c7be49ee809..7e5dedfa8912f 100644 --- a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx +++ b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx @@ -30,14 +30,17 @@ import { makeApi, JsonResponse, } from '@superset-ui/core'; +import { useSelector } from 'react-redux'; import moment from 'moment'; import rison from 'rison'; +import { UserWithPermissionsAndRoles as User } from 'src/types/bootstrapTypes'; import { DatasetRadioState, EXPLORE_CHART_DEFAULT, DatasetOwner, DatasetOptionAutocomplete, Query, + ExploreRootState, } from 'src/SqlLab/types'; import { Dataset } from '@superset-ui/chart-controls'; import { exploreChart } from 'src/explore/exploreUtils'; @@ -51,9 +54,6 @@ interface SaveDatasetModalProps { buttonTextOnOverwrite: string; modalDescription?: string; datasource?: ExploreDatasource; - user: { - userId: number; - }; query?: Query; actions?: Record; } @@ -113,7 +113,6 @@ export const SaveDatasetModal: FunctionComponent = ({ buttonTextOnSave, buttonTextOnOverwrite, modalDescription, - user, query, actions, }) => { @@ -135,6 +134,11 @@ export const SaveDatasetModal: FunctionComponent = ({ >({}); const [saveModalAutocompleteValue] = useState(''); + const user = useSelector(state => { + console.log('findme', state); + return state.explore.user; + }); + const handleOverwriteDataset = async () => { await updateDataset( query!.dbId, diff --git a/superset-frontend/src/SqlLab/types.ts b/superset-frontend/src/SqlLab/types.ts index 8c3235bcaf7ba..34a0747b384d9 100644 --- a/superset-frontend/src/SqlLab/types.ts +++ b/superset-frontend/src/SqlLab/types.ts @@ -109,7 +109,7 @@ export type toastState = { noDuplicate: boolean; }; -export type RootState = { +export type SqlLabRootState = { sqlLab: { activeSouthPaneTab: string | number; // default is string; action.newQuery.id is number alerts: any[]; @@ -129,6 +129,32 @@ export type RootState = { common: {}; }; +export type ExploreRootState = { + explore: { + can_add: boolean; + can_download: boolean; + common: object; + controls: object; + controlsTransferred: object; + datasource: object; + datasource_id: number; + datasource_type: string; + force: boolean; + forced_height: object; + form_data: object; + isDatasourceMetaLoading: boolean; + isStarred: boolean; + slice: object; + sliceName: string; + standalone: boolean; + timeFormattedColumns: object; + user: UserWithPermissionsAndRoles; + }; + localStorageUsageInKilobytes: number; + messageToasts: toastState[]; + common: {}; +}; + export enum DatasetRadioState { SAVE_NEW = 1, OVERWRITE_DATASET = 2, diff --git a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx index 67e5f172cd2ef..7e702fc7d16c3 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx @@ -49,9 +49,6 @@ export interface Props { actions: Partial & Pick; // we use this props control force update when this panel resize shouldForceUpdate?: number; - user: { - userId: number; - }; } const enableExploreDnd = isFeatureEnabled( @@ -187,7 +184,6 @@ export default function DataSourcePanel({ controls: { datasource: datasourceControl }, actions, shouldForceUpdate, - user, }: Props) { const { columns: _columns, metrics } = datasource; @@ -453,7 +449,6 @@ export default function DataSourcePanel({ onHide={() => setShowSaveDatasetModal(false)} buttonTextOnSave={t('Save')} buttonTextOnOverwrite={t('Overwrite')} - user={user} /> {datasource.id != null && mainBody} From 1d49ef520919e239e80fe1b63e3bb003779f7e6d Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Wed, 11 May 2022 14:22:35 -0500 Subject: [PATCH 15/57] Addressed comments --- .../src/SqlLab/components/ResultSet/index.tsx | 3 +- .../components/SaveDatasetModal/index.tsx | 129 +++++++++--------- .../components/TabbedSqlEditors/index.jsx | 1 + .../src/SqlLab/reducers/getInitialState.js | 1 + superset-frontend/src/SqlLab/types.ts | 2 + .../DatasourcePanel/DatasourcePanel.test.tsx | 24 ++-- .../components/DatasourcePanel/index.tsx | 3 + .../components/ExploreViewContainer/index.jsx | 1 + 8 files changed, 87 insertions(+), 77 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx index 226f28b18bf8b..be65de1079240 100644 --- a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx +++ b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx @@ -230,8 +230,7 @@ export default class ResultSet extends React.PureComponent< modalDescription={t( 'Save this query as a virtual dataset to continue exploring', )} - query={this.props.query} - actions={this.props.actions} + datasource={this.props.query} /> {this.props.visualize && diff --git a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx index 7e5dedfa8912f..38692734f5ecf 100644 --- a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx +++ b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx @@ -33,6 +33,8 @@ import { import { useSelector } from 'react-redux'; import moment from 'moment'; import rison from 'rison'; +import { createDatasource } from 'src/SqlLab/actions/sqlLab'; +import { addDangerToast } from 'src/components/MessageToasts/actions'; import { UserWithPermissionsAndRoles as User } from 'src/types/bootstrapTypes'; import { DatasetRadioState, @@ -40,12 +42,14 @@ import { DatasetOwner, DatasetOptionAutocomplete, Query, + SqlLabExploreRootState, + SqlLabRootState, ExploreRootState, } from 'src/SqlLab/types'; -import { Dataset } from '@superset-ui/chart-controls'; +// import { Dataset } from '@superset-ui/chart-controls'; import { exploreChart } from 'src/explore/exploreUtils'; -type ExploreDatasource = Dataset | Query; +// type ExploreDatasource = Dataset | Query; interface SaveDatasetModalProps { visible: boolean; @@ -53,30 +57,28 @@ interface SaveDatasetModalProps { buttonTextOnSave: string; buttonTextOnOverwrite: string; modalDescription?: string; - datasource?: ExploreDatasource; - query?: Query; - actions?: Record; + datasource: Query; } const Styles = styled.div` - .smd-body { + .sdm-body { margin: 0 8px; } - .smd-input { + .sdm-input { margin-left: 45px; width: 401px; } - .smd-autocomplete { + .sdm-autocomplete { margin-left: 8px; width: 401px; } - .smd-radio { + .sdm-radio { display: block; height: 30px; margin: 10px 0px; line-height: 30px; } - .smd-overwrite-msg { + .sdm-overwrite-msg { margin: 7px; } `; @@ -113,16 +115,12 @@ export const SaveDatasetModal: FunctionComponent = ({ buttonTextOnSave, buttonTextOnOverwrite, modalDescription, - query, - actions, + datasource, }) => { const getDefaultDatasetName = () => - `${query?.tab} ${moment().format('MM/DD/YYYY HH:mm:ss')}`; - - const [newSaveDatasetName, setNewSaveDatasetName] = useState( - getDefaultDatasetName(), - ); - const [saveDatasetRadioBtnState, setSaveDatasetRadioBtnState] = useState( + `${datasource.tab} ${moment().format('MM/DD/YYYY HH:mm:ss')}`; + const [datasetName, setDatasetName] = useState(getDefaultDatasetName()); + const [newOrOverwrite, setNewOrOverwrite] = useState( DatasetRadioState.SAVE_NEW, ); const [shouldOverwriteDataset, setShouldOverwriteDataset] = useState(false); @@ -132,20 +130,21 @@ export const SaveDatasetModal: FunctionComponent = ({ const [datasetToOverwrite, setDatasetToOverwrite] = useState< Record >({}); - const [saveModalAutocompleteValue] = useState(''); + const [autocompleteValue, setAutocompleteValue] = useState(''); - const user = useSelector(state => { - console.log('findme', state); + const user = useSelector(state => { + if (state.sqlLab) return state.sqlLab.user; return state.explore.user; }); + console.log('findme user', user); const handleOverwriteDataset = async () => { await updateDataset( - query!.dbId, + datasource.dbId, datasetToOverwrite.datasetId, - query!.sql, + datasource.sql, // TODO: lyndsiWilliams - Define d - query!.results.selected_columns.map((d: any) => ({ + datasource.results.selected_columns.map((d: any) => ({ column_name: d.name, type: d.type, is_dttm: d.is_dttm, @@ -156,13 +155,13 @@ export const SaveDatasetModal: FunctionComponent = ({ setShouldOverwriteDataset(false); setDatasetToOverwrite({}); - setNewSaveDatasetName(getDefaultDatasetName()); + setDatasetName(getDefaultDatasetName()); exploreChart({ ...EXPLORE_CHART_DEFAULT, datasource: `${datasetToOverwrite.datasetId}__table`, // TODO: lyndsiWilliams - Define d - all_columns: query!.results.selected_columns.map((d: any) => d.name), + all_columns: datasource.results.selected_columns.map((d: any) => d.name), }); }; @@ -204,40 +203,42 @@ export const SaveDatasetModal: FunctionComponent = ({ return null; }; + console.log('findme selectedColumns', datasource); const handleSaveInDataset = () => { // if user wants to overwrite a dataset we need to prompt them - if (saveDatasetRadioBtnState === DatasetRadioState.OVERWRITE_DATASET) { + if (newOrOverwrite === DatasetRadioState.OVERWRITE_DATASET) { setShouldOverwriteDataset(true); return; } - const selectedColumns = query?.results?.selected_columns || []; + const selectedColumns = datasource.results.selected_columns || []; // The filters param is only used to test jinja templates. // Remove the special filters entry from the templateParams // before saving the dataset. - if (query?.templateParams) { - const p = JSON.parse(query?.templateParams); + if (datasource.templateParams) { + const p = JSON.parse(datasource.templateParams); /* eslint-disable-next-line no-underscore-dangle */ if (p._filters) { /* eslint-disable-next-line no-underscore-dangle */ delete p._filters; // eslint-disable-next-line no-param-reassign - query.templateParams = JSON.stringify(p); + datasource.templateParams = JSON.stringify(p); } } // TODO: lyndsiWilliams - set up when the back end logic is implemented - actions - ?.createDatasource({ - schema: query?.schema, - sql: query?.sql, - dbId: query?.dbId, - templateParams: query?.templateParams, - datasourceName: newSaveDatasetName, + new Promise( + createDatasource({ + schema: datasource.schema, + sql: datasource.sql, + dbId: datasource.dbId, + templateParams: datasource.templateParams, + datasourceName: datasetName, columns: selectedColumns, - }) + }), + ) .then((data: { table_id: number }) => { exploreChart({ datasource: `${data.table_id}__table`, @@ -250,10 +251,10 @@ export const SaveDatasetModal: FunctionComponent = ({ }); }) .catch(() => { - actions.addDangerToast(t('An error occurred saving dataset')); + addDangerToast(t('An error occurred saving dataset')); }); - setNewSaveDatasetName(getDefaultDatasetName()); + setDatasetName(getDefaultDatasetName()); onHide(); }; @@ -269,11 +270,7 @@ export const SaveDatasetModal: FunctionComponent = ({ const handleDatasetNameChange = (e: React.FormEvent) => { // @ts-expect-error - setNewSaveDatasetName(e.target.value); - }; - - const handleSaveDatasetRadioBtnState = (e: RadioChangeEvent) => { - setSaveDatasetRadioBtnState(Number(e.target.value)); + setDatasetName(e.target.value); }; const handleOverwriteCancel = () => { @@ -282,11 +279,11 @@ export const SaveDatasetModal: FunctionComponent = ({ }; const disableSaveAndExploreBtn = - (saveDatasetRadioBtnState === DatasetRadioState.SAVE_NEW && - newSaveDatasetName.length === 0) || - (saveDatasetRadioBtnState === DatasetRadioState.OVERWRITE_DATASET && + (newOrOverwrite === DatasetRadioState.SAVE_NEW && + datasetName.length === 0) || + (newOrOverwrite === DatasetRadioState.OVERWRITE_DATASET && Object.keys(datasetToOverwrite).length === 0 && - saveModalAutocompleteValue.length === 0); + autocompleteValue.length === 0); const filterAutocompleteOption = ( inputValue: string, @@ -327,41 +324,47 @@ export const SaveDatasetModal: FunctionComponent = ({ > {!shouldOverwriteDataset && ( -
+
{modalDescription && ( -
{modalDescription}
+
{modalDescription}
)} { + setNewOrOverwrite(Number(e.target.value)); + }} + value={newOrOverwrite} > - + {t('Save as new')} - + {t('Overwrite existing')} setDatasetToOverwrite({})} + onChange={value => { + setDatasetToOverwrite({}); + setAutocompleteValue(value); + }} placeholder={t('Select or type dataset name')} filterOption={filterAutocompleteOption} - disabled={saveDatasetRadioBtnState !== 2} + disabled={newOrOverwrite !== 2} + value={autocompleteValue} />
)} {shouldOverwriteDataset && ( -
+
{t('Are you sure you want to overwrite this dataset?')}
)} diff --git a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx index 494ef9cba0ef7..cd27e37625fef 100644 --- a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx +++ b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx @@ -319,6 +319,7 @@ class TabbedSqlEditors extends React.PureComponent { } render() { + console.log('findme TABBEDSQLEDITORS'); const noQueryEditors = this.props.queryEditors?.length === 0; const editors = this.props.queryEditors.map(qe => { let latestQuery; diff --git a/superset-frontend/src/SqlLab/reducers/getInitialState.js b/superset-frontend/src/SqlLab/reducers/getInitialState.js index d5f02029d0ccf..c08292f013c09 100644 --- a/superset-frontend/src/SqlLab/reducers/getInitialState.js +++ b/superset-frontend/src/SqlLab/reducers/getInitialState.js @@ -37,6 +37,7 @@ export default function getInitialState({ * To allow for a transparent migration, the initial state is a combination * of the backend state (if any) with the browser state (if any). */ + console.log('findme INITIALSTATE'); const queryEditors = []; const defaultQueryEditor = { id: null, diff --git a/superset-frontend/src/SqlLab/types.ts b/superset-frontend/src/SqlLab/types.ts index 34a0747b384d9..775b599031c60 100644 --- a/superset-frontend/src/SqlLab/types.ts +++ b/superset-frontend/src/SqlLab/types.ts @@ -155,6 +155,8 @@ export type ExploreRootState = { common: {}; }; +export type SqlLabExploreRootState = SqlLabRootState | ExploreRootState; + export enum DatasetRadioState { SAVE_NEW = 1, OVERWRITE_DATASET = 2, diff --git a/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx b/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx index a6e84b54ec447..875e9b34c9662 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx @@ -78,9 +78,6 @@ const props: DatasourcePanelProps = { actions: { setControlValue: jest.fn(), }, - user: { - userId: 1, - }, }; const setup = (props: DatasourcePanelProps) => ( @@ -95,19 +92,19 @@ function search(value: string, input: HTMLElement) { } test('should render', () => { - const { container } = render(setup(props)); + const { container } = render(setup(props), { useRedux: true }); expect(container).toBeVisible(); }); test('should display items in controls', () => { - render(setup(props)); + render(setup(props), { useRedux: true }); expect(screen.getByText('birth_names')).toBeInTheDocument(); expect(screen.getByText('Metrics')).toBeInTheDocument(); expect(screen.getByText('Columns')).toBeInTheDocument(); }); test('should render the metrics', () => { - render(setup(props)); + render(setup(props), { useRedux: true }); const metricsNum = metrics.length; metrics.forEach(metric => expect(screen.getByText(metric.metric_name)).toBeInTheDocument(), @@ -118,7 +115,7 @@ test('should render the metrics', () => { }); test('should render the columns', () => { - render(setup(props)); + render(setup(props), { useRedux: true }); const columnsNum = columns.length; columns.forEach(col => expect(screen.getByText(col.column_name)).toBeInTheDocument(), @@ -129,7 +126,7 @@ test('should render the columns', () => { }); test('should render 0 search results', async () => { - render(setup(props)); + render(setup(props), { useRedux: true }); const searchInput = screen.getByPlaceholderText('Search Metrics & Columns'); search('nothing', searchInput); @@ -137,7 +134,7 @@ test('should render 0 search results', async () => { }); test('should search and render matching columns', async () => { - render(setup(props)); + render(setup(props), { useRedux: true }); const searchInput = screen.getByPlaceholderText('Search Metrics & Columns'); search(columns[0].column_name, searchInput); @@ -149,7 +146,7 @@ test('should search and render matching columns', async () => { }); test('should search and render matching metrics', async () => { - render(setup(props)); + render(setup(props), { useRedux: true }); const searchInput = screen.getByPlaceholderText('Search Metrics & Columns'); search(metrics[0].metric_name, searchInput); @@ -177,6 +174,7 @@ test('should render a warning', async () => { }, }, }), + { useRedux: true }, ); expect( await screen.findByRole('img', { name: 'alert-solid' }), @@ -189,9 +187,10 @@ test('should render a create dataset infobox', () => { ...props, datasource: { ...datasource, - type: DatasourceType.SavedQuery, + type: DatasourceType.Query, }, }), + { useRedux: true }, ); const createButton = screen.getByRole('button', { @@ -209,9 +208,10 @@ test('should render a save dataset modal when "Create a dataset" is clicked', () ...props, datasource: { ...datasource, - type: DatasourceType.SavedQuery, + type: DatasourceType.Query, }, }), + { useRedux: true }, ); const createButton = screen.getByRole('button', { diff --git a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx index 7e702fc7d16c3..f23088514c3be 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx @@ -33,6 +33,7 @@ import { FAST_DEBOUNCE } from 'src/constants'; import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags'; import { ExploreActions } from 'src/explore/actions/exploreActions'; import Control from 'src/explore/components/Control'; +import { Query } from 'src/SqlLab/types'; import DatasourcePanelDragOption from './DatasourcePanelDragOption'; import { DndItemType } from '../DndItemType'; import { StyledColumnOption, StyledMetricOption } from '../optionRenderers'; @@ -322,6 +323,7 @@ export default function DataSourcePanel({
{datasource.type === DatasourceType.Dataset || datasource.type === DatasourceType.SlTable || + datasource.type === DatasourceType.Query || (datasource.type === DatasourceType.SavedQuery && showInfoboxCheck() && ( @@ -449,6 +451,7 @@ export default function DataSourcePanel({ onHide={() => setShowSaveDatasetModal(false)} buttonTextOnSave={t('Save')} buttonTextOnOverwrite={t('Overwrite')} + datasource={datasource as unknown as Query} /> {datasource.id != null && mainBody} diff --git a/superset-frontend/src/explore/components/ExploreViewContainer/index.jsx b/superset-frontend/src/explore/components/ExploreViewContainer/index.jsx index 79d372122003b..6a5afd80d3025 100644 --- a/superset-frontend/src/explore/components/ExploreViewContainer/index.jsx +++ b/superset-frontend/src/explore/components/ExploreViewContainer/index.jsx @@ -197,6 +197,7 @@ const updateHistory = debounce( ); function ExploreViewContainer(props) { + console.log('findme evc', props); const dynamicPluginContext = usePluginContext(); const dynamicPlugin = dynamicPluginContext.dynamicPlugins[props.vizType]; const isDynamicPluginLoading = dynamicPlugin && dynamicPlugin.mounting; From 4c35710e106ab0e16bd69af3f11bff6db58951df Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Fri, 13 May 2022 11:56:05 -0500 Subject: [PATCH 16/57] Adjusting to new query type --- .../superset-ui-chart-controls/src/types.ts | 3 +- .../superset-ui-core/src/query/types/Query.ts | 150 ++++++++++++++++++ .../components/SaveDatasetModal/index.tsx | 20 ++- superset-frontend/src/SqlLab/types.ts | 16 ++ 4 files changed, 180 insertions(+), 9 deletions(-) diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/types.ts b/superset-frontend/packages/superset-ui-chart-controls/src/types.ts index 662bc9d0b3e38..baa66f0310a32 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/types.ts +++ b/superset-frontend/packages/superset-ui-chart-controls/src/types.ts @@ -25,6 +25,7 @@ import type { JsonValue, Metric, QueryFormData, + Query, } from '@superset-ui/core'; import { sharedControls } from './shared-controls'; import sharedControlComponents from './shared-controls/components'; @@ -69,7 +70,7 @@ export interface Dataset { export interface ControlPanelState { form_data: QueryFormData; - datasource: Dataset | null; + datasource: Dataset | Query | null; controls: ControlStateMapping; } diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts b/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts index c9961cc7cb415..a73c2233cd7e3 100644 --- a/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts +++ b/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts @@ -165,4 +165,154 @@ export interface QueryContext { form_data?: QueryFormData; } +export const ErrorTypeEnum = { + // Frontend errors + FRONTEND_CSRF_ERROR: 'FRONTEND_CSRF_ERROR', + FRONTEND_NETWORK_ERROR: 'FRONTEND_NETWORK_ERROR', + FRONTEND_TIMEOUT_ERROR: 'FRONTEND_TIMEOUT_ERROR', + + // DB Engine errors + GENERIC_DB_ENGINE_ERROR: 'GENERIC_DB_ENGINE_ERROR', + COLUMN_DOES_NOT_EXIST_ERROR: 'COLUMN_DOES_NOT_EXIST_ERROR', + TABLE_DOES_NOT_EXIST_ERROR: 'TABLE_DOES_NOT_EXIST_ERROR', + SCHEMA_DOES_NOT_EXIST_ERROR: 'SCHEMA_DOES_NOT_EXIST_ERROR', + CONNECTION_INVALID_USERNAME_ERROR: 'CONNECTION_INVALID_USERNAME_ERROR', + CONNECTION_INVALID_PASSWORD_ERROR: 'CONNECTION_INVALID_PASSWORD_ERROR', + CONNECTION_INVALID_HOSTNAME_ERROR: 'CONNECTION_INVALID_HOSTNAME_ERROR', + CONNECTION_PORT_CLOSED_ERROR: 'CONNECTION_PORT_CLOSED_ERROR', + CONNECTION_INVALID_PORT_ERROR: 'CONNECTION_INVALID_PORT_ERROR', + CONNECTION_HOST_DOWN_ERROR: 'CONNECTION_HOST_DOWN_ERROR', + CONNECTION_ACCESS_DENIED_ERROR: 'CONNECTION_ACCESS_DENIED_ERROR', + CONNECTION_UNKNOWN_DATABASE_ERROR: 'CONNECTION_UNKNOWN_DATABASE_ERROR', + CONNECTION_DATABASE_PERMISSIONS_ERROR: + 'CONNECTION_DATABASE_PERMISSIONS_ERROR', + CONNECTION_MISSING_PARAMETERS_ERRORS: 'CONNECTION_MISSING_PARAMETERS_ERRORS', + OBJECT_DOES_NOT_EXIST_ERROR: 'OBJECT_DOES_NOT_EXIST_ERROR', + SYNTAX_ERROR: 'SYNTAX_ERROR', + + // Viz errors + VIZ_GET_DF_ERROR: 'VIZ_GET_DF_ERROR', + UNKNOWN_DATASOURCE_TYPE_ERROR: 'UNKNOWN_DATASOURCE_TYPE_ERROR', + FAILED_FETCHING_DATASOURCE_INFO_ERROR: + 'FAILED_FETCHING_DATASOURCE_INFO_ERROR', + + // Security access errors + TABLE_SECURITY_ACCESS_ERROR: 'TABLE_SECURITY_ACCESS_ERROR', + DATASOURCE_SECURITY_ACCESS_ERROR: 'DATASOURCE_SECURITY_ACCESS_ERROR', + DATABASE_SECURITY_ACCESS_ERROR: 'DATABASE_SECURITY_ACCESS_ERROR', + QUERY_SECURITY_ACCESS_ERROR: 'QUERY_SECURITY_ACCESS_ERROR', + MISSING_OWNERSHIP_ERROR: 'MISSING_OWNERSHIP_ERROR', + + // Other errors + BACKEND_TIMEOUT_ERROR: 'BACKEND_TIMEOUT_ERROR', + DATABASE_NOT_FOUND_ERROR: 'DATABASE_NOT_FOUND_ERROR', + + // Sqllab error + MISSING_TEMPLATE_PARAMS_ERROR: 'MISSING_TEMPLATE_PARAMS_ERROR', + INVALID_TEMPLATE_PARAMS_ERROR: 'INVALID_TEMPLATE_PARAMS_ERROR', + RESULTS_BACKEND_NOT_CONFIGURED_ERROR: 'RESULTS_BACKEND_NOT_CONFIGURED_ERROR', + DML_NOT_ALLOWED_ERROR: 'DML_NOT_ALLOWED_ERROR', + INVALID_CTAS_QUERY_ERROR: 'INVALID_CTAS_QUERY_ERROR', + INVALID_CVAS_QUERY_ERROR: 'INVALID_CVAS_QUERY_ERROR', + SQLLAB_TIMEOUT_ERROR: 'SQLLAB_TIMEOUT_ERROR', + RESULTS_BACKEND_ERROR: 'RESULTS_BACKEND_ERROR', + ASYNC_WORKERS_ERROR: 'ASYNC_WORKERS_ERROR', + + // Generic errors + GENERIC_COMMAND_ERROR: 'GENERIC_COMMAND_ERROR', + GENERIC_BACKEND_ERROR: 'GENERIC_BACKEND_ERROR', + + // API errors + INVALID_PAYLOAD_FORMAT_ERROR: 'INVALID_PAYLOAD_FORMAT_ERROR', + INVALID_PAYLOAD_SCHEMA_ERROR: 'INVALID_PAYLOAD_SCHEMA_ERROR', +} as const; + +type ValueOf = T[keyof T]; + +export type ErrorType = ValueOf; + +// Keep in sync with superset/views/errors.py +export type ErrorLevel = 'info' | 'warning' | 'error'; + +export type ErrorSource = 'dashboard' | 'explore' | 'sqllab'; + +export type SupersetError | null> = { + error_type: ErrorType; + extra: ExtraType; + level: ErrorLevel; + message: string; +}; + +export const CtasEnum = { + TABLE: 'TABLE', + VIEW: 'VIEW', +}; + +export type QueryColumn = { + name: string; + type: string | null; + is_dttm: boolean; +}; + +export type QueryState = + | 'stopped' + | 'failed' + | 'pending' + | 'running' + | 'scheduled' + | 'success' + | 'fetching' + | 'timed_out'; + +export type Query = { + cached: boolean; + ctas: boolean; + ctas_method?: keyof typeof CtasEnum; + dbId: number; + errors?: SupersetError[]; + errorMessage: string | null; + extra: { + progress: string | null; + }; + id: string; + isDataPreview: boolean; + link?: string; + progress: number; + results: { + displayLimitReached: boolean; + columns: QueryColumn[]; + data: Record[]; + expanded_columns: QueryColumn[]; + selected_columns: QueryColumn[]; + query: { limit: number }; + }; + resultsKey: string | null; + schema?: string; + sql: string; + sqlEditorId: string; + state: QueryState; + tab: string | null; + tempSchema: string | null; + tempTable: string; + trackingUrl: string | null; + templateParams: any; + rows: number; + queryLimit: number; + limitingFactor: string; + endDttm: number; + duration: string; + startDttm: number; + time: Record; + user: Record; + userId: number; + db: Record; + started: string; + querylink: Record; + queryId: number; + executedSql: string; + output: string | Record; + actions: Record; + type: string; +}; + export default {}; diff --git a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx index 38692734f5ecf..d46b9388501bd 100644 --- a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx +++ b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx @@ -29,6 +29,7 @@ import { SupersetClient, makeApi, JsonResponse, + // DatasourceType, } from '@superset-ui/core'; import { useSelector } from 'react-redux'; import moment from 'moment'; @@ -43,8 +44,7 @@ import { DatasetOptionAutocomplete, Query, SqlLabExploreRootState, - SqlLabRootState, - ExploreRootState, + getInitialState, } from 'src/SqlLab/types'; // import { Dataset } from '@superset-ui/chart-controls'; import { exploreChart } from 'src/explore/exploreUtils'; @@ -108,6 +108,11 @@ const updateDataset = async ( return data.json.result; }; +// const getDatasourceState = (datasource: ExploreDatasource) => { +// if (datasource.type === DatasourceType.Dataset) return datasource as Dataset; +// return datasource as Query; +// }; + // eslint-disable-next-line no-empty-pattern export const SaveDatasetModal: FunctionComponent = ({ visible, @@ -132,11 +137,9 @@ export const SaveDatasetModal: FunctionComponent = ({ >({}); const [autocompleteValue, setAutocompleteValue] = useState(''); - const user = useSelector(state => { - if (state.sqlLab) return state.sqlLab.user; - return state.explore.user; - }); - console.log('findme user', user); + const user = useSelector(state => + getInitialState(state), + ); const handleOverwriteDataset = async () => { await updateDataset( @@ -203,7 +206,6 @@ export const SaveDatasetModal: FunctionComponent = ({ return null; }; - console.log('findme selectedColumns', datasource); const handleSaveInDataset = () => { // if user wants to overwrite a dataset we need to prompt them @@ -228,6 +230,8 @@ export const SaveDatasetModal: FunctionComponent = ({ } } + console.log('findme save state', datasource, datasetName, selectedColumns); + // TODO: lyndsiWilliams - set up when the back end logic is implemented new Promise( createDatasource({ diff --git a/superset-frontend/src/SqlLab/types.ts b/superset-frontend/src/SqlLab/types.ts index 775b599031c60..e97eaeafb1827 100644 --- a/superset-frontend/src/SqlLab/types.ts +++ b/superset-frontend/src/SqlLab/types.ts @@ -20,6 +20,7 @@ import { SupersetError } from 'src/components/ErrorMessage/types'; import { CtasEnum } from 'src/SqlLab/actions/sqlLab'; import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes'; import { ToastType } from 'src/components/MessageToasts/types'; +import { DatasourceType } from '@superset-ui/core'; // same as superset.result_set.ResultSetColumnType export type Column = { @@ -86,6 +87,7 @@ export type Query = { executedSql: string; output: string | Record; actions: Record; + type: DatasourceType.Query; }; export interface QueryEditor { @@ -157,6 +159,20 @@ export type ExploreRootState = { export type SqlLabExploreRootState = SqlLabRootState | ExploreRootState; +export const getInitialState = (state: SqlLabExploreRootState) => { + if (state.hasOwnProperty('sqlLab')) { + const { + sqlLab: { user }, + } = state as SqlLabRootState; + return user; + } + + const { + explore: { user }, + } = state as ExploreRootState; + return user; +}; + export enum DatasetRadioState { SAVE_NEW = 1, OVERWRITE_DATASET = 2, From ff63e6aeae2e7024435389d68fcdb6ed33555e13 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Tue, 17 May 2022 09:06:19 -0500 Subject: [PATCH 17/57] Fixed save dataset in explore and union type --- .../ExploreCtasResultsButton/index.tsx | 4 +- .../components/SaveDatasetModal/index.tsx | 122 ++++++++++++------ .../components/TabbedSqlEditors/index.jsx | 1 - .../src/SqlLab/reducers/getInitialState.js | 1 - .../components/DatasourcePanel/index.tsx | 2 +- .../components/ExploreViewContainer/index.jsx | 1 - 6 files changed, 83 insertions(+), 48 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/ExploreCtasResultsButton/index.tsx b/superset-frontend/src/SqlLab/components/ExploreCtasResultsButton/index.tsx index 9adb5dc40261a..fbcdc15bc5a37 100644 --- a/superset-frontend/src/SqlLab/components/ExploreCtasResultsButton/index.tsx +++ b/superset-frontend/src/SqlLab/components/ExploreCtasResultsButton/index.tsx @@ -22,7 +22,7 @@ import { t } from '@superset-ui/core'; import { InfoTooltipWithTrigger } from '@superset-ui/chart-controls'; import Button from 'src/components/Button'; import { exploreChart } from 'src/explore/exploreUtils'; -import { RootState } from 'src/SqlLab/types'; +import { SqlLabRootState } from 'src/SqlLab/types'; interface ExploreCtasResultsButtonProps { actions: { @@ -45,7 +45,7 @@ const ExploreCtasResultsButton = ({ }: ExploreCtasResultsButtonProps) => { const { createCtasDatasource, addInfoToast, addDangerToast } = actions; const errorMessage = useSelector( - (state: RootState) => state.sqlLab.errorMessage, + (state: SqlLabRootState) => state.sqlLab.errorMessage, ); const buildVizOptions = { diff --git a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx index d46b9388501bd..058c6dcf0709c 100644 --- a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx +++ b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx @@ -29,9 +29,10 @@ import { SupersetClient, makeApi, JsonResponse, + JsonObject, // DatasourceType, } from '@superset-ui/core'; -import { useSelector } from 'react-redux'; +import { useSelector, useDispatch } from 'react-redux'; import moment from 'moment'; import rison from 'rison'; import { createDatasource } from 'src/SqlLab/actions/sqlLab'; @@ -46,10 +47,10 @@ import { SqlLabExploreRootState, getInitialState, } from 'src/SqlLab/types'; -// import { Dataset } from '@superset-ui/chart-controls'; +import { Dataset } from '@superset-ui/chart-controls'; import { exploreChart } from 'src/explore/exploreUtils'; -// type ExploreDatasource = Dataset | Query; +type ExploreDatasource = Dataset | Query; interface SaveDatasetModalProps { visible: boolean; @@ -57,7 +58,7 @@ interface SaveDatasetModalProps { buttonTextOnSave: string; buttonTextOnOverwrite: string; modalDescription?: string; - datasource: Query; + datasource: ExploreDatasource; } const Styles = styled.div` @@ -108,11 +109,6 @@ const updateDataset = async ( return data.json.result; }; -// const getDatasourceState = (datasource: ExploreDatasource) => { -// if (datasource.type === DatasourceType.Dataset) return datasource as Dataset; -// return datasource as Query; -// }; - // eslint-disable-next-line no-empty-pattern export const SaveDatasetModal: FunctionComponent = ({ visible, @@ -122,8 +118,17 @@ export const SaveDatasetModal: FunctionComponent = ({ modalDescription, datasource, }) => { - const getDefaultDatasetName = () => - `${datasource.tab} ${moment().format('MM/DD/YYYY HH:mm:ss')}`; + const query = datasource as Query; + const dataset = datasource as Dataset; + const datasourceIsQuery = datasource.hasOwnProperty('tab'); + const getDefaultDatasetName = () => { + if (datasourceIsQuery) { + return `${query.tab} ${moment().format('MM/DD/YYYY HH:mm:ss')}`; + } + return `${dataset.datasource_name} ${moment().format( + 'MM/DD/YYYY HH:mm:ss', + )}`; + }; const [datasetName, setDatasetName] = useState(getDefaultDatasetName()); const [newOrOverwrite, setNewOrOverwrite] = useState( DatasetRadioState.SAVE_NEW, @@ -140,21 +145,40 @@ export const SaveDatasetModal: FunctionComponent = ({ const user = useSelector(state => getInitialState(state), ); + const dispatch = useDispatch<(dispatch: any) => Promise>(); const handleOverwriteDataset = async () => { - await updateDataset( - datasource.dbId, - datasetToOverwrite.datasetId, - datasource.sql, - // TODO: lyndsiWilliams - Define d - datasource.results.selected_columns.map((d: any) => ({ - column_name: d.name, - type: d.type, - is_dttm: d.is_dttm, - })), - datasetToOverwrite.owners.map((o: DatasetOwner) => o.id), - true, - ); + const unionUpdateDataset = () => { + if (datasourceIsQuery) { + return updateDataset( + query.dbId, + datasetToOverwrite.datasetId, + query.sql, + // TODO: lyndsiWilliams - Define d + query.results.selected_columns.map((d: any) => ({ + column_name: d.name, + type: d.type, + is_dttm: d.is_dttm, + })), + datasetToOverwrite.owners.map((o: DatasetOwner) => o.id), + true, + ); + } + return updateDataset( + dataset.id, + datasetToOverwrite.datasetId, + dataset.main_dttm_col, + // TODO: lyndsiWilliams - Define d + dataset.columns.map((d: any) => ({ + column_name: d.name, + type: d.type, + is_dttm: d.is_dttm, + })), + datasetToOverwrite.owners.map((o: DatasetOwner) => o.id), + true, + ); + }; + await unionUpdateDataset(); setShouldOverwriteDataset(false); setDatasetToOverwrite({}); @@ -164,7 +188,9 @@ export const SaveDatasetModal: FunctionComponent = ({ ...EXPLORE_CHART_DEFAULT, datasource: `${datasetToOverwrite.datasetId}__table`, // TODO: lyndsiWilliams - Define d - all_columns: datasource.results.selected_columns.map((d: any) => d.name), + all_columns: datasourceIsQuery + ? query.results.selected_columns.map((d: any) => d.name) + : dataset.columns.map((d: any) => d.name), }); }; @@ -214,35 +240,45 @@ export const SaveDatasetModal: FunctionComponent = ({ return; } - const selectedColumns = datasource.results.selected_columns || []; + const querySelectedColumns = query.results.selected_columns || []; + const datasetSelectedColumns = dataset.columns || []; // The filters param is only used to test jinja templates. // Remove the special filters entry from the templateParams // before saving the dataset. - if (datasource.templateParams) { - const p = JSON.parse(datasource.templateParams); + if (query.templateParams) { + const p = JSON.parse(query.templateParams); /* eslint-disable-next-line no-underscore-dangle */ if (p._filters) { /* eslint-disable-next-line no-underscore-dangle */ delete p._filters; // eslint-disable-next-line no-param-reassign - datasource.templateParams = JSON.stringify(p); + query.templateParams = JSON.stringify(p); } } - console.log('findme save state', datasource, datasetName, selectedColumns); - - // TODO: lyndsiWilliams - set up when the back end logic is implemented - new Promise( - createDatasource({ - schema: datasource.schema, - sql: datasource.sql, - dbId: datasource.dbId, - templateParams: datasource.templateParams, + const unionCreateDatasource = () => { + if (datasourceIsQuery) { + return createDatasource({ + schema: query.schema, + sql: query.sql, + dbId: query.dbId, + templateParams: query.templateParams, + datasourceName: datasetName, + columns: querySelectedColumns, + }); + } + return createDatasource({ + schema: dataset.description, + sql: dataset.main_dttm_col, + dbId: dataset.id, + templateParams: dataset.time_grain_sqla, datasourceName: datasetName, - columns: selectedColumns, - }), - ) + columns: datasetSelectedColumns, + }); + }; + + dispatch(unionCreateDatasource) .then((data: { table_id: number }) => { exploreChart({ datasource: `${data.table_id}__table`, @@ -250,7 +286,9 @@ export const SaveDatasetModal: FunctionComponent = ({ groupby: [], time_range: 'No filter', viz_type: 'table', - all_columns: selectedColumns.map(c => c.name), + all_columns: datasourceIsQuery + ? querySelectedColumns.map(c => c.name) + : datasetSelectedColumns.map(c => c.name), row_limit: 1000, }); }) diff --git a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx index cd27e37625fef..494ef9cba0ef7 100644 --- a/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx +++ b/superset-frontend/src/SqlLab/components/TabbedSqlEditors/index.jsx @@ -319,7 +319,6 @@ class TabbedSqlEditors extends React.PureComponent { } render() { - console.log('findme TABBEDSQLEDITORS'); const noQueryEditors = this.props.queryEditors?.length === 0; const editors = this.props.queryEditors.map(qe => { let latestQuery; diff --git a/superset-frontend/src/SqlLab/reducers/getInitialState.js b/superset-frontend/src/SqlLab/reducers/getInitialState.js index c08292f013c09..d5f02029d0ccf 100644 --- a/superset-frontend/src/SqlLab/reducers/getInitialState.js +++ b/superset-frontend/src/SqlLab/reducers/getInitialState.js @@ -37,7 +37,6 @@ export default function getInitialState({ * To allow for a transparent migration, the initial state is a combination * of the backend state (if any) with the browser state (if any). */ - console.log('findme INITIALSTATE'); const queryEditors = []; const defaultQueryEditor = { id: null, diff --git a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx index f23088514c3be..6d18e15e024ff 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx @@ -451,7 +451,7 @@ export default function DataSourcePanel({ onHide={() => setShowSaveDatasetModal(false)} buttonTextOnSave={t('Save')} buttonTextOnOverwrite={t('Overwrite')} - datasource={datasource as unknown as Query} + datasource={datasource} /> {datasource.id != null && mainBody} diff --git a/superset-frontend/src/explore/components/ExploreViewContainer/index.jsx b/superset-frontend/src/explore/components/ExploreViewContainer/index.jsx index 6a5afd80d3025..79d372122003b 100644 --- a/superset-frontend/src/explore/components/ExploreViewContainer/index.jsx +++ b/superset-frontend/src/explore/components/ExploreViewContainer/index.jsx @@ -197,7 +197,6 @@ const updateHistory = debounce( ); function ExploreViewContainer(props) { - console.log('findme evc', props); const dynamicPluginContext = usePluginContext(); const dynamicPlugin = dynamicPluginContext.dynamicPlugins[props.vizType]; const isDynamicPluginLoading = dynamicPlugin && dynamicPlugin.mounting; From d28edf471c018947ce44b3466aadb00aa028843f Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Tue, 17 May 2022 09:21:44 -0500 Subject: [PATCH 18/57] Added testing --- .../DatasourcePanel/DatasourcePanel.test.tsx | 16 ++++++ .../components/DatasourcePanel/index.tsx | 51 +++++++++---------- 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx b/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx index 875e9b34c9662..ce307e3f905ce 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx @@ -193,6 +193,7 @@ test('should render a create dataset infobox', () => { { useRedux: true }, ); + screen.logTestingPlaygroundURL(); const createButton = screen.getByRole('button', { name: /create a dataset/i, }); @@ -224,3 +225,18 @@ test('should render a save dataset modal when "Create a dataset" is clicked', () expect(saveDatasetModalTitle).toBeVisible(); }); + +test('should not render a save dataset modal when datasource is not query or dataset', () => { + render( + setup({ + ...props, + datasource: { + ...datasource, + type: DatasourceType.Table, + }, + }), + { useRedux: true }, + ); + + expect(screen.queryByText(/create a dataset/i)).toBe(null); +}); diff --git a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx index 6d18e15e024ff..f8873cc56996a 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx @@ -323,33 +323,30 @@ export default function DataSourcePanel({
{datasource.type === DatasourceType.Dataset || datasource.type === DatasourceType.SlTable || - datasource.type === DatasourceType.Query || - (datasource.type === DatasourceType.SavedQuery && - showInfoboxCheck() && ( - - - sessionStorage.setItem('showInfobox', 'false') - } - type="info" - message="" - description={ - <> - setShowSaveDatasetModal(true)} - className="add-dataset-alert-description" - > - {t('Create a dataset')} - - {t(' to edit or add columns and metrics.')} - - } - /> - - ))} + datasource.type === DatasourceType.SavedQuery || + (datasource.type === DatasourceType.Query && showInfoboxCheck() && ( + + sessionStorage.setItem('showInfobox', 'false')} + type="info" + message="" + description={ + <> + setShowSaveDatasetModal(true)} + className="add-dataset-alert-description" + > + {t('Create a dataset')} + + {t(' to edit or add columns and metrics.')} + + } + /> + + ))} Date: Tue, 17 May 2022 09:29:48 -0500 Subject: [PATCH 19/57] Defined d for queries --- .../components/SaveDatasetModal/index.tsx | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx index 058c6dcf0709c..18eb3439f3700 100644 --- a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx +++ b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx @@ -154,12 +154,13 @@ export const SaveDatasetModal: FunctionComponent = ({ query.dbId, datasetToOverwrite.datasetId, query.sql, - // TODO: lyndsiWilliams - Define d - query.results.selected_columns.map((d: any) => ({ - column_name: d.name, - type: d.type, - is_dttm: d.is_dttm, - })), + query.results.selected_columns.map( + (d: { name: string; type: string; is_dttm: boolean }) => ({ + column_name: d.name, + type: d.type, + is_dttm: d.is_dttm, + }), + ), datasetToOverwrite.owners.map((o: DatasetOwner) => o.id), true, ); @@ -168,7 +169,7 @@ export const SaveDatasetModal: FunctionComponent = ({ dataset.id, datasetToOverwrite.datasetId, dataset.main_dttm_col, - // TODO: lyndsiWilliams - Define d + // TODO: lyndsiWilliams - Define dataset d dataset.columns.map((d: any) => ({ column_name: d.name, type: d.type, @@ -187,9 +188,11 @@ export const SaveDatasetModal: FunctionComponent = ({ exploreChart({ ...EXPLORE_CHART_DEFAULT, datasource: `${datasetToOverwrite.datasetId}__table`, - // TODO: lyndsiWilliams - Define d + // TODO: lyndsiWilliams - Define dataset d all_columns: datasourceIsQuery - ? query.results.selected_columns.map((d: any) => d.name) + ? query.results.selected_columns.map( + (d: { name: string; type: string; is_dttm: boolean }) => d.name, + ) : dataset.columns.map((d: any) => d.name), }); }; From 65da6ac17909fba59610c933acfb7c087b86bbeb Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Tue, 17 May 2022 12:33:02 -0500 Subject: [PATCH 20/57] Remove dataset from SaveDatasetModal --- .../components/SaveDatasetModal/index.tsx | 98 +++++-------------- 1 file changed, 27 insertions(+), 71 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx index 18eb3439f3700..cbdd9e427d035 100644 --- a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx +++ b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx @@ -119,16 +119,8 @@ export const SaveDatasetModal: FunctionComponent = ({ datasource, }) => { const query = datasource as Query; - const dataset = datasource as Dataset; - const datasourceIsQuery = datasource.hasOwnProperty('tab'); - const getDefaultDatasetName = () => { - if (datasourceIsQuery) { - return `${query.tab} ${moment().format('MM/DD/YYYY HH:mm:ss')}`; - } - return `${dataset.datasource_name} ${moment().format( - 'MM/DD/YYYY HH:mm:ss', - )}`; - }; + const getDefaultDatasetName = () => + `${query.tab} ${moment().format('MM/DD/YYYY HH:mm:ss')}`; const [datasetName, setDatasetName] = useState(getDefaultDatasetName()); const [newOrOverwrite, setNewOrOverwrite] = useState( DatasetRadioState.SAVE_NEW, @@ -148,38 +140,20 @@ export const SaveDatasetModal: FunctionComponent = ({ const dispatch = useDispatch<(dispatch: any) => Promise>(); const handleOverwriteDataset = async () => { - const unionUpdateDataset = () => { - if (datasourceIsQuery) { - return updateDataset( - query.dbId, - datasetToOverwrite.datasetId, - query.sql, - query.results.selected_columns.map( - (d: { name: string; type: string; is_dttm: boolean }) => ({ - column_name: d.name, - type: d.type, - is_dttm: d.is_dttm, - }), - ), - datasetToOverwrite.owners.map((o: DatasetOwner) => o.id), - true, - ); - } - return updateDataset( - dataset.id, - datasetToOverwrite.datasetId, - dataset.main_dttm_col, - // TODO: lyndsiWilliams - Define dataset d - dataset.columns.map((d: any) => ({ + await updateDataset( + query.dbId, + datasetToOverwrite.datasetId, + query.sql, + query.results.selected_columns.map( + (d: { name: string; type: string; is_dttm: boolean }) => ({ column_name: d.name, type: d.type, is_dttm: d.is_dttm, - })), - datasetToOverwrite.owners.map((o: DatasetOwner) => o.id), - true, - ); - }; - await unionUpdateDataset(); + }), + ), + datasetToOverwrite.owners.map((o: DatasetOwner) => o.id), + true, + ); setShouldOverwriteDataset(false); setDatasetToOverwrite({}); @@ -188,12 +162,9 @@ export const SaveDatasetModal: FunctionComponent = ({ exploreChart({ ...EXPLORE_CHART_DEFAULT, datasource: `${datasetToOverwrite.datasetId}__table`, - // TODO: lyndsiWilliams - Define dataset d - all_columns: datasourceIsQuery - ? query.results.selected_columns.map( - (d: { name: string; type: string; is_dttm: boolean }) => d.name, - ) - : dataset.columns.map((d: any) => d.name), + all_columns: query.results.selected_columns.map( + (d: { name: string; type: string; is_dttm: boolean }) => d.name, + ), }); }; @@ -243,8 +214,7 @@ export const SaveDatasetModal: FunctionComponent = ({ return; } - const querySelectedColumns = query.results.selected_columns || []; - const datasetSelectedColumns = dataset.columns || []; + const selectedColumns = query.results.selected_columns || []; // The filters param is only used to test jinja templates. // Remove the special filters entry from the templateParams @@ -260,28 +230,16 @@ export const SaveDatasetModal: FunctionComponent = ({ } } - const unionCreateDatasource = () => { - if (datasourceIsQuery) { - return createDatasource({ - schema: query.schema, - sql: query.sql, - dbId: query.dbId, - templateParams: query.templateParams, - datasourceName: datasetName, - columns: querySelectedColumns, - }); - } - return createDatasource({ - schema: dataset.description, - sql: dataset.main_dttm_col, - dbId: dataset.id, - templateParams: dataset.time_grain_sqla, + dispatch( + createDatasource({ + schema: query.schema, + sql: query.sql, + dbId: query.dbId, + templateParams: query.templateParams, datasourceName: datasetName, - columns: datasetSelectedColumns, - }); - }; - - dispatch(unionCreateDatasource) + columns: selectedColumns, + }), + ) .then((data: { table_id: number }) => { exploreChart({ datasource: `${data.table_id}__table`, @@ -289,9 +247,7 @@ export const SaveDatasetModal: FunctionComponent = ({ groupby: [], time_range: 'No filter', viz_type: 'table', - all_columns: datasourceIsQuery - ? querySelectedColumns.map(c => c.name) - : datasetSelectedColumns.map(c => c.name), + all_columns: selectedColumns.map(c => c.name), row_limit: 1000, }); }) From 2ef4ad48ac74a611e704f8d9289c0b7575f64aa4 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Tue, 17 May 2022 12:44:49 -0500 Subject: [PATCH 21/57] Clarify useSelector parameter --- .../src/SqlLab/components/SaveDatasetModal/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx index cbdd9e427d035..8169f69a6c184 100644 --- a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx +++ b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx @@ -134,8 +134,8 @@ export const SaveDatasetModal: FunctionComponent = ({ >({}); const [autocompleteValue, setAutocompleteValue] = useState(''); - const user = useSelector(state => - getInitialState(state), + const user = useSelector(user => + getInitialState(user), ); const dispatch = useDispatch<(dispatch: any) => Promise>(); From 1729fe78df511fa2eb26c0977500cd6811fbdbdd Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Wed, 18 May 2022 07:50:46 -0500 Subject: [PATCH 22/57] Fix dndControls union type --- .../src/shared-controls/dndControls.tsx | 64 +++++++++++-------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx index 44e0d2fb6381c..51880fa88a3f7 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx +++ b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx @@ -23,7 +23,7 @@ import { t, validateNonEmpty, } from '@superset-ui/core'; -import { ExtraControlProps, SharedControlConfig } from '../types'; +import { ExtraControlProps, SharedControlConfig, Dataset } from '../types'; import { TIME_COLUMN_OPTION, TIME_FILTER_LABELS } from '../constants'; export const dndGroupByControl: SharedControlConfig<'DndColumnSelect'> = { @@ -36,15 +36,16 @@ export const dndGroupByControl: SharedControlConfig<'DndColumnSelect'> = { ), mapStateToProps(state, { includeTime }) { const newState: ExtraControlProps = {}; + const dataset = state.datasource as Dataset; if (state.datasource) { - const options = state.datasource.columns.filter(c => c.groupby); + const options = dataset.columns.filter(c => c.groupby); if (includeTime) { options.unshift(TIME_COLUMN_OPTION); } newState.options = Object.fromEntries( options.map(option => [option.column_name, option]), ); - newState.savedMetrics = state.datasource.metrics || []; + newState.savedMetrics = dataset.metrics || []; } return newState; }, @@ -82,14 +83,18 @@ export const dnd_adhoc_filters: SharedControlConfig<'DndFilterSelect'> = { label: t('Filters'), default: [], description: '', - mapStateToProps: ({ datasource, form_data }) => ({ - columns: datasource?.columns.filter(c => c.filterable) || [], - savedMetrics: datasource?.metrics || [], - // current active adhoc metrics - selectedMetrics: - form_data.metrics || (form_data.metric ? [form_data.metric] : []), - datasource, - }), + mapStateToProps: ({ datasource, form_data }) => { + const dataset = datasource as Dataset; + + return { + columns: dataset?.columns.filter(c => c.filterable) || [], + savedMetrics: dataset?.metrics || [], + // current active adhoc metrics + selectedMetrics: + form_data.metrics || (form_data.metric ? [form_data.metric] : []), + datasource, + }; + }, provideFormDataToProps: true, }; @@ -98,12 +103,16 @@ export const dnd_adhoc_metrics: SharedControlConfig<'DndMetricSelect'> = { multi: true, label: t('Metrics'), validators: [validateNonEmpty], - mapStateToProps: ({ datasource }) => ({ - columns: datasource ? datasource.columns : [], - savedMetrics: datasource ? datasource.metrics : [], - datasource, - datasourceType: datasource?.type, - }), + mapStateToProps: ({ datasource }) => { + const dataset = datasource as Dataset; + + return { + columns: dataset?.columns || [], + savedMetrics: dataset?.metrics || [], + datasource, + datasourceType: dataset?.type, + }; + }, description: t('One or many metrics to display'), }; @@ -128,12 +137,16 @@ export const dnd_sort_by: SharedControlConfig<'DndMetricSelect'> = { 'Metric used to define how the top series are sorted if a series or row limit is present. ' + 'If undefined reverts to the first metric (where appropriate).', ), - mapStateToProps: ({ datasource }) => ({ - columns: datasource?.columns || [], - savedMetrics: datasource?.metrics || [], - datasource, - datasourceType: datasource?.type, - }), + mapStateToProps: ({ datasource }) => { + const dataset = datasource as Dataset; + + return { + columns: dataset?.columns || [], + savedMetrics: dataset?.metrics || [], + datasource, + datasourceType: dataset?.type, + }; + }, }; export const dnd_size: SharedControlConfig<'DndMetricSelect'> = { @@ -178,14 +191,15 @@ export const dnd_granularity_sqla: typeof dndGroupByControl = { : 'Drop temporal column here', ), mapStateToProps: ({ datasource }) => { - const temporalColumns = datasource?.columns.filter(c => c.is_dttm) ?? []; + const dataset = datasource as Dataset; + const temporalColumns = dataset?.columns.filter(c => c.is_dttm) ?? []; const options = Object.fromEntries( temporalColumns.map(option => [option.column_name, option]), ); return { options, default: - datasource?.main_dttm_col || temporalColumns[0]?.column_name || null, + dataset?.main_dttm_col || temporalColumns[0]?.column_name || null, isTemporal: true, }; }, From 8a45aef7b93d9c68ab6980f0926887144df1f13b Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Wed, 18 May 2022 07:56:50 -0500 Subject: [PATCH 23/57] Fix shared-controls union type --- .../src/shared-controls/index.tsx | 65 +++++++++++-------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx index 713fbf1c2dd27..8027bf2040923 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx +++ b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx @@ -63,6 +63,7 @@ import { ColumnMeta, ExtraControlProps, SelectControlConfig, + Dataset, } from '../types'; import { ColumnOption } from '../components/ColumnOption'; @@ -131,8 +132,9 @@ const groupByControl: SharedControlConfig<'SelectControl', ColumnMeta> = { promptTextCreator: (label: unknown) => label, mapStateToProps(state, { includeTime }) { const newState: ExtraControlProps = {}; + const dataset = state.datasource as Dataset; if (state.datasource) { - const options = state.datasource.columns.filter(c => c.groupby); + const options = dataset.columns.filter(c => c.groupby); if (includeTime) { options.unshift(TIME_COLUMN_OPTION); } @@ -148,12 +150,16 @@ const metrics: SharedControlConfig<'MetricsControl'> = { multi: true, label: t('Metrics'), validators: [validateNonEmpty], - mapStateToProps: ({ datasource }) => ({ - columns: datasource ? datasource.columns : [], - savedMetrics: datasource ? datasource.metrics : [], - datasource, - datasourceType: datasource?.type, - }), + mapStateToProps: ({ datasource }) => { + const dataset = datasource as Dataset; + + return { + columns: dataset?.columns || [], + savedMetrics: dataset?.metrics || [], + datasource, + datasourceType: dataset?.type, + }; + }, description: t('One or many metrics to display'), }; @@ -293,11 +299,12 @@ const granularity_sqla: SharedControlConfig<'SelectControl', ColumnMeta> = { valueKey: 'column_name', mapStateToProps: state => { const props: Partial> = {}; + const dataset = state.datasource as Dataset; if (state.datasource) { - props.options = state.datasource.columns.filter(c => c.is_dttm); + props.options = dataset.columns.filter(c => c.is_dttm); props.default = null; - if (state.datasource.main_dttm_col) { - props.default = state.datasource.main_dttm_col; + if (dataset.main_dttm_col) { + props.default = dataset.main_dttm_col; } else if (props.options && props.options.length > 0) { props.default = props.options[0].column_name; } @@ -318,7 +325,7 @@ const time_grain_sqla: SharedControlConfig<'SelectControl'> = { 'engine basis in the Superset source code.', ), mapStateToProps: ({ datasource }) => ({ - choices: datasource?.time_grain_sqla || null, + choices: (datasource as Dataset)?.time_grain_sqla || null, }), }; @@ -335,7 +342,7 @@ const time_range: SharedControlConfig<'DateFilterControl'> = { "using the engine's local timezone. Note one can explicitly set the timezone " + 'per the ISO 8601 format if specifying either the start and/or end time.', ), - mapStateToProps: ({ datasource, form_data }) => ({ + mapStateToProps: ({ datasource }) => ({ datasource, }), }; @@ -399,12 +406,15 @@ const sort_by: SharedControlConfig<'MetricsControl'> = { 'Metric used to define how the top series are sorted if a series or row limit is present. ' + 'If undefined reverts to the first metric (where appropriate).', ), - mapStateToProps: ({ datasource }) => ({ - columns: datasource?.columns || [], - savedMetrics: datasource?.metrics || [], - datasource, - datasourceType: datasource?.type, - }), + mapStateToProps: ({ datasource }) => { + const dataset = datasource as Dataset; + return { + columns: dataset?.columns || [], + savedMetrics: dataset?.metrics || [], + datasource, + datasourceType: dataset?.type, + }; + }, }; const series: typeof groupByControl = { @@ -492,14 +502,17 @@ const adhoc_filters: SharedControlConfig<'AdhocFilterControl'> = { label: t('Filters'), default: [], description: '', - mapStateToProps: ({ datasource, form_data }) => ({ - columns: datasource?.columns.filter(c => c.filterable) || [], - savedMetrics: datasource?.metrics || [], - // current active adhoc metrics - selectedMetrics: - form_data.metrics || (form_data.metric ? [form_data.metric] : []), - datasource, - }), + mapStateToProps: ({ datasource, form_data }) => { + const dataset = datasource as Dataset; + return { + columns: dataset?.columns.filter(c => c.filterable) || [], + savedMetrics: dataset?.metrics || [], + // current active adhoc metrics + selectedMetrics: + form_data.metrics || (form_data.metric ? [form_data.metric] : []), + datasource, + }; + }, }; const color_scheme: SharedControlConfig<'ColorSchemeControl'> = { From e4fc40496f562152b89055d7a05f59515de666f5 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Wed, 18 May 2022 07:59:35 -0500 Subject: [PATCH 24/57] Fix controlPanel union type --- .../src/controlPanel.tsx | 5 +++-- .../src/controlPanel.ts | 3 ++- .../src/controlPanel.ts | 9 +++++--- .../src/plugin/controls/columns.tsx | 5 +++-- .../src/plugin/controls/metrics.tsx | 11 +++++----- .../src/plugin/controls/orderBy.tsx | 4 ++-- .../src/plugin/controlPanel.tsx | 4 +++- .../plugin-chart-table/src/controlPanel.tsx | 22 +++++++++++-------- .../components/DatasourcePanel/index.tsx | 2 +- superset-frontend/src/explore/fixtures.tsx | 3 ++- 10 files changed, 41 insertions(+), 27 deletions(-) diff --git a/superset-frontend/plugins/legacy-plugin-chart-event-flow/src/controlPanel.tsx b/superset-frontend/plugins/legacy-plugin-chart-event-flow/src/controlPanel.tsx index 11a0b88987883..7c6cf1fd53aaa 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-event-flow/src/controlPanel.tsx +++ b/superset-frontend/plugins/legacy-plugin-chart-event-flow/src/controlPanel.tsx @@ -27,6 +27,7 @@ import { formatSelectOptionsForRange, sections, SelectControlConfig, + Dataset, } from '@superset-ui/chart-controls'; const config: ControlPanelConfig = { @@ -44,7 +45,7 @@ const config: ControlPanelConfig = { label: t('Event Names'), description: t('Columns to display'), mapStateToProps: state => ({ - choices: columnChoices(state?.datasource), + choices: columnChoices(state?.datasource as Dataset), }), // choices is from `mapStateToProps` default: (control: ControlState) => @@ -109,7 +110,7 @@ const config: ControlPanelConfig = { valueKey: 'column_name', allowAll: true, mapStateToProps: state => ({ - options: state.datasource ? state.datasource.columns : [], + options: (state.datasource as Dataset)?.columns || [], }), commaChoosesOption: false, freeForm: true, diff --git a/superset-frontend/plugins/legacy-plugin-chart-histogram/src/controlPanel.ts b/superset-frontend/plugins/legacy-plugin-chart-histogram/src/controlPanel.ts index 26cec94749e6e..3bc2e2ea7960d 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-histogram/src/controlPanel.ts +++ b/superset-frontend/plugins/legacy-plugin-chart-histogram/src/controlPanel.ts @@ -29,6 +29,7 @@ import { formatSelectOptions, sections, dndColumnsControl, + Dataset, } from '@superset-ui/chart-controls'; const allColumns = { @@ -37,7 +38,7 @@ const allColumns = { default: null, description: t('Select the numeric columns to draw the histogram'), mapStateToProps: (state: ControlPanelState) => ({ - choices: columnChoices(state.datasource), + choices: columnChoices(state.datasource as Dataset), }), multi: true, validators: [validateNonEmpty], diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/controlPanel.ts b/superset-frontend/plugins/legacy-plugin-chart-map-box/src/controlPanel.ts index 8642e8946fe03..8a9055b8dfbc1 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/controlPanel.ts +++ b/superset-frontend/plugins/legacy-plugin-chart-map-box/src/controlPanel.ts @@ -24,13 +24,14 @@ import { formatSelectOptions, sections, dndEntity, + Dataset, } from '@superset-ui/chart-controls'; const allColumns = { type: 'SelectControl', default: null, mapStateToProps: (state: ControlPanelState) => ({ - choices: columnChoices(state.datasource), + choices: columnChoices(state.datasource as Dataset), }), }; @@ -122,7 +123,9 @@ const config: ControlPanelConfig = { 'on the largest cluster', ), mapStateToProps: state => { - const datasourceChoices = columnChoices(state.datasource); + const datasourceChoices = columnChoices( + state.datasource as Dataset, + ); const choices: [string, string][] = formatSelectOptions([ 'Auto', ]); @@ -167,7 +170,7 @@ const config: ControlPanelConfig = { 'Leave empty to get a count of points in each cluster.', ), mapStateToProps: state => ({ - choices: columnChoices(state.datasource), + choices: columnChoices(state.datasource as Dataset), }), }, }, diff --git a/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/columns.tsx b/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/columns.tsx index fd24bb75fbb20..24fd182c557d6 100644 --- a/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/columns.tsx +++ b/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/columns.tsx @@ -21,6 +21,7 @@ import { ControlSetItem, ExtraControlProps, sharedControls, + Dataset, } from '@superset-ui/chart-controls'; import { ensureIsArray, @@ -44,7 +45,7 @@ const allColumns: typeof sharedControls.groupby = { valueRenderer: c => , valueKey: 'column_name', mapStateToProps: ({ datasource, controls }, controlState) => ({ - options: datasource?.columns || [], + options: (datasource as Dataset)?.columns || [], queryMode: getQueryMode(controls), externalValidationErrors: isRawMode({ controls }) && ensureIsArray(controlState.value).length === 0 @@ -63,7 +64,7 @@ const dndAllColumns: typeof sharedControls.groupby = { mapStateToProps({ datasource, controls }, controlState) { const newState: ExtraControlProps = {}; if (datasource) { - const options = datasource.columns; + const options = (datasource as Dataset).columns; newState.options = Object.fromEntries( options.map(option => [option.column_name, option]), ); diff --git a/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/metrics.tsx b/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/metrics.tsx index 7df35e6a668cb..ea006ff62c824 100644 --- a/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/metrics.tsx +++ b/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/metrics.tsx @@ -21,6 +21,7 @@ import { ControlSetItem, ControlState, sharedControls, + Dataset, } from '@superset-ui/chart-controls'; import { FeatureFlag, isFeatureEnabled, t } from '@superset-ui/core'; import { getQueryMode, isAggMode, validateAggControlValues } from './shared'; @@ -35,10 +36,10 @@ const percentMetrics: typeof sharedControls.metrics = { visibility: isAggMode, resetOnHide: false, mapStateToProps: ({ datasource, controls }, controlState) => ({ - columns: datasource?.columns || [], - savedMetrics: datasource?.metrics || [], + columns: (datasource as Dataset)?.columns || [], + savedMetrics: (datasource as Dataset)?.metrics || [], datasource, - datasourceType: datasource?.type, + datasourceType: (datasource as Dataset)?.type, queryMode: getQueryMode(controls), externalValidationErrors: validateAggControlValues(controls, [ controls.groupby?.value, @@ -74,8 +75,8 @@ export const metricsControlSetItem: ControlSetItem = { { controls, datasource, form_data }: ControlPanelState, controlState: ControlState, ) => ({ - columns: datasource?.columns.filter(c => c.filterable) || [], - savedMetrics: datasource?.metrics || [], + columns: (datasource as Dataset)?.columns.filter(c => c.filterable) || [], + savedMetrics: (datasource as Dataset)?.metrics || [], // current active adhoc metrics selectedMetrics: form_data.metrics || (form_data.metric ? [form_data.metric] : []), diff --git a/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/orderBy.tsx b/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/orderBy.tsx index b7c8f8e2406bf..0466b5a155e93 100644 --- a/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/orderBy.tsx +++ b/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/orderBy.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { ControlSetItem } from '@superset-ui/chart-controls'; +import { ControlSetItem, Dataset } from '@superset-ui/chart-controls'; import { t } from '@superset-ui/core'; import { isAggMode, isRawMode } from './shared'; @@ -29,7 +29,7 @@ export const orderByControlSetItem: ControlSetItem = { multi: true, default: [], mapStateToProps: ({ datasource }) => ({ - choices: datasource?.order_by_choices || [], + choices: (datasource as Dataset)?.order_by_choices || [], }), visibility: isRawMode, resetOnHide: false, diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/controlPanel.tsx index 99efb8bbcce9f..9b89b661c64f2 100644 --- a/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/controlPanel.tsx @@ -30,6 +30,7 @@ import { sections, sharedControls, emitFilterControl, + Dataset, } from '@superset-ui/chart-controls'; import { MetricsLayoutEnum } from '../types'; @@ -349,7 +350,8 @@ const config: ControlPanelConfig = { const values = (explore?.controls?.metrics?.value as QueryFormMetric[]) ?? []; - const verboseMap = explore?.datasource?.verbose_map ?? {}; + const verboseMap = + (explore?.datasource as Dataset)?.verbose_map ?? {}; const metricColumn = values.map(value => { if (typeof value === 'string') { return { value, label: verboseMap[value] ?? value }; diff --git a/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx index bb855bd7ccc56..5f2d65ce8fd3e 100644 --- a/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx @@ -44,6 +44,7 @@ import { ExtraControlProps, ControlState, emitFilterControl, + Dataset, } from '@superset-ui/chart-controls'; import i18n from './i18n'; @@ -109,7 +110,7 @@ const all_columns: typeof sharedControls.groupby = { valueRenderer: c => , valueKey: 'column_name', mapStateToProps: ({ datasource, controls }, controlState) => ({ - options: datasource?.columns || [], + options: (datasource as Dataset)?.columns || [], queryMode: getQueryMode(controls), externalValidationErrors: isRawMode({ controls }) && ensureIsArray(controlState.value).length === 0 @@ -128,7 +129,7 @@ const dnd_all_columns: typeof sharedControls.groupby = { mapStateToProps({ datasource, controls }, controlState) { const newState: ExtraControlProps = {}; if (datasource) { - const options = datasource.columns; + const options = (datasource as Dataset).columns; newState.options = Object.fromEntries( options.map(option => [option.column_name, option]), ); @@ -154,10 +155,10 @@ const percent_metrics: typeof sharedControls.metrics = { visibility: isAggMode, resetOnHide: false, mapStateToProps: ({ datasource, controls }, controlState) => ({ - columns: datasource?.columns || [], - savedMetrics: datasource?.metrics || [], + columns: (datasource as Dataset)?.columns || [], + savedMetrics: (datasource as Dataset)?.metrics || [], datasource, - datasourceType: datasource?.type, + datasourceType: (datasource as Dataset)?.type, queryMode: getQueryMode(controls), externalValidationErrors: validateAggControlValues(controls, [ controls.groupby?.value, @@ -229,8 +230,10 @@ const config: ControlPanelConfig = { { controls, datasource, form_data }: ControlPanelState, controlState: ControlState, ) => ({ - columns: datasource?.columns.filter(c => c.filterable) || [], - savedMetrics: datasource?.metrics || [], + columns: + (datasource as Dataset)?.columns.filter(c => c.filterable) || + [], + savedMetrics: (datasource as Dataset)?.metrics || [], // current active adhoc metrics selectedMetrics: form_data.metrics || @@ -280,7 +283,7 @@ const config: ControlPanelConfig = { multi: true, default: [], mapStateToProps: ({ datasource }) => ({ - choices: datasource?.order_by_choices || [], + choices: (datasource as Dataset)?.order_by_choices || [], }), visibility: isRawMode, resetOnHide: false, @@ -505,7 +508,8 @@ const config: ControlPanelConfig = { return true; }, mapStateToProps(explore, _, chart) { - const verboseMap = explore?.datasource?.verbose_map ?? {}; + const verboseMap = + (explore?.datasource as Dataset)?.verbose_map ?? {}; const { colnames, coltypes } = chart?.queriesResponse?.[0] ?? {}; const numericColumns = diff --git a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx index f8873cc56996a..17dd982886be9 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx @@ -39,7 +39,7 @@ import { DndItemType } from '../DndItemType'; import { StyledColumnOption, StyledMetricOption } from '../optionRenderers'; interface DatasourceControl extends ControlConfig { - datasource?: Dataset; + datasource?: Dataset | Query; } export interface Props { diff --git a/superset-frontend/src/explore/fixtures.tsx b/superset-frontend/src/explore/fixtures.tsx index 7ce2626069df2..4cb9d9d42cb87 100644 --- a/superset-frontend/src/explore/fixtures.tsx +++ b/superset-frontend/src/explore/fixtures.tsx @@ -24,6 +24,7 @@ import { ColumnOption, ControlConfig, ControlPanelSectionConfig, + Dataset, } from '@superset-ui/chart-controls'; export const controlPanelSectionsChartOptions: (ControlPanelSectionConfig | null)[] = @@ -99,7 +100,7 @@ export const controlPanelSectionsChartOptionsTable: ControlPanelSectionConfig[] optionRenderer: c => , valueKey: 'column_name', mapStateToProps: stateRef => ({ - options: stateRef.datasource ? stateRef.datasource.columns : [], + options: (stateRef.datasource as Dataset)?.columns || [], }), freeForm: true, } as ControlConfig<'SelectControl', ColumnMeta>, From 106609abfccc0745611e53d01930bcad2ed737f3 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Thu, 19 May 2022 06:35:41 -0500 Subject: [PATCH 25/57] Move ExploreRootState to explore type file --- superset-frontend/src/SqlLab/types.ts | 27 +------------------------ superset-frontend/src/explore/types.ts | 28 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/superset-frontend/src/SqlLab/types.ts b/superset-frontend/src/SqlLab/types.ts index e97eaeafb1827..f30e16371a57e 100644 --- a/superset-frontend/src/SqlLab/types.ts +++ b/superset-frontend/src/SqlLab/types.ts @@ -21,6 +21,7 @@ import { CtasEnum } from 'src/SqlLab/actions/sqlLab'; import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes'; import { ToastType } from 'src/components/MessageToasts/types'; import { DatasourceType } from '@superset-ui/core'; +import { ExploreRootState } from 'src/explore/types'; // same as superset.result_set.ResultSetColumnType export type Column = { @@ -131,32 +132,6 @@ export type SqlLabRootState = { common: {}; }; -export type ExploreRootState = { - explore: { - can_add: boolean; - can_download: boolean; - common: object; - controls: object; - controlsTransferred: object; - datasource: object; - datasource_id: number; - datasource_type: string; - force: boolean; - forced_height: object; - form_data: object; - isDatasourceMetaLoading: boolean; - isStarred: boolean; - slice: object; - sliceName: string; - standalone: boolean; - timeFormattedColumns: object; - user: UserWithPermissionsAndRoles; - }; - localStorageUsageInKilobytes: number; - messageToasts: toastState[]; - common: {}; -}; - export type SqlLabExploreRootState = SqlLabRootState | ExploreRootState; export const getInitialState = (state: SqlLabExploreRootState) => { diff --git a/superset-frontend/src/explore/types.ts b/superset-frontend/src/explore/types.ts index 50ef57b9bb40b..4d50b449c500f 100644 --- a/superset-frontend/src/explore/types.ts +++ b/superset-frontend/src/explore/types.ts @@ -24,6 +24,8 @@ import { } from '@superset-ui/core'; import { ColumnMeta, Dataset } from '@superset-ui/chart-controls'; import { DatabaseObject } from 'src/views/CRUD/types'; +import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes'; +import { toastState } from 'src/SqlLab/types'; export { Slice, Chart } from 'src/types/Chart'; @@ -62,3 +64,29 @@ export type Datasource = Dataset & { schema?: string; is_sqllab_view?: boolean; }; + +export type ExploreRootState = { + explore: { + can_add: boolean; + can_download: boolean; + common: object; + controls: object; + controlsTransferred: object; + datasource: object; + datasource_id: number; + datasource_type: string; + force: boolean; + forced_height: object; + form_data: object; + isDatasourceMetaLoading: boolean; + isStarred: boolean; + slice: object; + sliceName: string; + standalone: boolean; + timeFormattedColumns: object; + user: UserWithPermissionsAndRoles; + }; + localStorageUsageInKilobytes: number; + messageToasts: toastState[]; + common: {}; +}; From 402804399dfb344499938e0a5a281b121b09c29d Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Thu, 19 May 2022 06:36:35 -0500 Subject: [PATCH 26/57] Remove unnecessary testing playground --- .../explore/components/DatasourcePanel/DatasourcePanel.test.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx b/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx index ce307e3f905ce..4b19c5b2f3818 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/DatasourcePanel.test.tsx @@ -193,7 +193,6 @@ test('should render a create dataset infobox', () => { { useRedux: true }, ); - screen.logTestingPlaygroundURL(); const createButton = screen.getByRole('button', { name: /create a dataset/i, }); From a9212fff37d3fe5f96d08a97ca694a0331937245 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Thu, 19 May 2022 06:40:19 -0500 Subject: [PATCH 27/57] Move datasource type check in DatasourcePanel to a function --- .../components/DatasourcePanel/index.tsx | 60 +++++++++++-------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx index 17dd982886be9..32b69198364cd 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx @@ -307,6 +307,17 @@ export default function DataSourcePanel({ return true; }; + const datasourceTypeCheck = () => { + if ( + datasource.type === DatasourceType.Dataset || + datasource.type === DatasourceType.SlTable || + datasource.type === DatasourceType.SavedQuery || + datasource.type === DatasourceType.Query + ) + return true; + return false; + }; + const mainBody = useMemo( () => ( <> @@ -321,32 +332,29 @@ export default function DataSourcePanel({ placeholder={t('Search Metrics & Columns')} />
- {datasource.type === DatasourceType.Dataset || - datasource.type === DatasourceType.SlTable || - datasource.type === DatasourceType.SavedQuery || - (datasource.type === DatasourceType.Query && showInfoboxCheck() && ( - - sessionStorage.setItem('showInfobox', 'false')} - type="info" - message="" - description={ - <> - setShowSaveDatasetModal(true)} - className="add-dataset-alert-description" - > - {t('Create a dataset')} - - {t(' to edit or add columns and metrics.')} - - } - /> - - ))} + {datasourceTypeCheck() && showInfoboxCheck() && ( + + sessionStorage.setItem('showInfobox', 'false')} + type="info" + message="" + description={ + <> + setShowSaveDatasetModal(true)} + className="add-dataset-alert-description" + > + {t('Create a dataset')} + + {t(' to edit or add columns and metrics.')} + + } + /> + + )} Date: Thu, 19 May 2022 06:59:44 -0500 Subject: [PATCH 28/57] Make all sqllab Query imports reference @superset-ui/core Query type --- .../superset-ui-core/src/query/types/Query.ts | 2 +- .../SqlLab/components/QueryHistory/index.tsx | 3 +- .../SqlLab/components/QuerySearch/index.tsx | 3 +- .../components/QueryStateLabel/index.tsx | 2 +- .../SqlLab/components/QueryTable/index.tsx | 4 +- .../src/SqlLab/components/ResultSet/index.tsx | 3 +- .../components/SaveDatasetModal/index.tsx | 3 +- .../SqlLab/components/TabStatusIcon/index.tsx | 2 +- superset-frontend/src/SqlLab/types.ts | 71 +------------------ .../components/DatasourcePanel/index.tsx | 3 +- 10 files changed, 11 insertions(+), 85 deletions(-) diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts b/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts index a73c2233cd7e3..83104be64af4e 100644 --- a/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts +++ b/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts @@ -312,7 +312,7 @@ export type Query = { executedSql: string; output: string | Record; actions: Record; - type: string; + type: DatasourceType.Query; }; export default {}; diff --git a/superset-frontend/src/SqlLab/components/QueryHistory/index.tsx b/superset-frontend/src/SqlLab/components/QueryHistory/index.tsx index c41ace1ead01b..56f7f42d8f900 100644 --- a/superset-frontend/src/SqlLab/components/QueryHistory/index.tsx +++ b/superset-frontend/src/SqlLab/components/QueryHistory/index.tsx @@ -18,8 +18,7 @@ */ import React from 'react'; import { EmptyStateMedium } from 'src/components/EmptyState'; -import { t, styled } from '@superset-ui/core'; -import { Query } from 'src/SqlLab/types'; +import { t, styled, Query } from '@superset-ui/core'; import QueryTable from 'src/SqlLab/components/QueryTable'; interface QueryHistoryProps { diff --git a/superset-frontend/src/SqlLab/components/QuerySearch/index.tsx b/superset-frontend/src/SqlLab/components/QuerySearch/index.tsx index e1e994133a9cc..17bdbe1cb1180 100644 --- a/superset-frontend/src/SqlLab/components/QuerySearch/index.tsx +++ b/superset-frontend/src/SqlLab/components/QuerySearch/index.tsx @@ -19,7 +19,7 @@ import React, { useState, useEffect } from 'react'; import Button from 'src/components/Button'; import Select from 'src/components/Select'; -import { styled, t, SupersetClient } from '@superset-ui/core'; +import { styled, t, SupersetClient, Query } from '@superset-ui/core'; import { debounce } from 'lodash'; import Loading from 'src/components/Loading'; import { @@ -29,7 +29,6 @@ import { epochTimeXYearsAgo, } from 'src/utils/dates'; import AsyncSelect from 'src/components/AsyncSelect'; -import { Query } from 'src/SqlLab/types'; import { STATUS_OPTIONS, TIME_OPTIONS } from 'src/SqlLab/constants'; import QueryTable from '../QueryTable'; diff --git a/superset-frontend/src/SqlLab/components/QueryStateLabel/index.tsx b/superset-frontend/src/SqlLab/components/QueryStateLabel/index.tsx index b2704843dfaec..6168a2af713a4 100644 --- a/superset-frontend/src/SqlLab/components/QueryStateLabel/index.tsx +++ b/superset-frontend/src/SqlLab/components/QueryStateLabel/index.tsx @@ -19,7 +19,7 @@ import React from 'react'; import Label from 'src/components/Label'; import { STATE_TYPE_MAP } from 'src/SqlLab/constants'; -import { Query } from 'src/SqlLab/types'; +import { Query } from '@superset-ui/core'; interface QueryStateLabelProps { query: Query; diff --git a/superset-frontend/src/SqlLab/components/QueryTable/index.tsx b/superset-frontend/src/SqlLab/components/QueryTable/index.tsx index b860feb89af07..1c7ea57b3c0d5 100644 --- a/superset-frontend/src/SqlLab/components/QueryTable/index.tsx +++ b/superset-frontend/src/SqlLab/components/QueryTable/index.tsx @@ -21,14 +21,14 @@ import moment from 'moment'; import Card from 'src/components/Card'; import ProgressBar from 'src/components/ProgressBar'; import Label from 'src/components/Label'; -import { t, useTheme } from '@superset-ui/core'; +import { t, useTheme, Query } from '@superset-ui/core'; import { useSelector } from 'react-redux'; import TableView from 'src/components/TableView'; import Button from 'src/components/Button'; import { fDuration } from 'src/utils/dates'; import Icons from 'src/components/Icons'; import { Tooltip } from 'src/components/Tooltip'; -import { Query, SqlLabRootState } from 'src/SqlLab/types'; +import { SqlLabRootState } from 'src/SqlLab/types'; import ModalTrigger from 'src/components/ModalTrigger'; import { UserWithPermissionsAndRoles as User } from 'src/types/bootstrapTypes'; import ResultSet from '../ResultSet'; diff --git a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx index be65de1079240..a04dd2c9ce5cd 100644 --- a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx +++ b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx @@ -21,7 +21,7 @@ import ButtonGroup from 'src/components/ButtonGroup'; import Alert from 'src/components/Alert'; import Button from 'src/components/Button'; import shortid from 'shortid'; -import { styled, t } from '@superset-ui/core'; +import { styled, t, Query } from '@superset-ui/core'; import ErrorMessageWithStackTrace from 'src/components/ErrorMessage/ErrorMessageWithStackTrace'; import { SaveDatasetModal } from 'src/SqlLab/components/SaveDatasetModal'; import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes'; @@ -33,7 +33,6 @@ import FilterableTable, { import CopyToClipboard from 'src/components/CopyToClipboard'; import { prepareCopyToClipboardTabularData } from 'src/utils/common'; import { CtasEnum } from 'src/SqlLab/actions/sqlLab'; -import { Query } from 'src/SqlLab/types'; import ExploreCtasResultsButton from '../ExploreCtasResultsButton'; import ExploreResultsButton from '../ExploreResultsButton'; import HighlightedSql from '../HighlightedSql'; diff --git a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx index 8169f69a6c184..56cd978067f09 100644 --- a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx +++ b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx @@ -30,7 +30,7 @@ import { makeApi, JsonResponse, JsonObject, - // DatasourceType, + Query, } from '@superset-ui/core'; import { useSelector, useDispatch } from 'react-redux'; import moment from 'moment'; @@ -43,7 +43,6 @@ import { EXPLORE_CHART_DEFAULT, DatasetOwner, DatasetOptionAutocomplete, - Query, SqlLabExploreRootState, getInitialState, } from 'src/SqlLab/types'; diff --git a/superset-frontend/src/SqlLab/components/TabStatusIcon/index.tsx b/superset-frontend/src/SqlLab/components/TabStatusIcon/index.tsx index 070e749288eaa..799124fb9c099 100644 --- a/superset-frontend/src/SqlLab/components/TabStatusIcon/index.tsx +++ b/superset-frontend/src/SqlLab/components/TabStatusIcon/index.tsx @@ -17,7 +17,7 @@ * under the License. */ import React from 'react'; -import { QueryState } from 'src/SqlLab/types'; +import { QueryState } from '@superset-ui/core'; interface TabStatusIconProps { tabState: QueryState; diff --git a/superset-frontend/src/SqlLab/types.ts b/superset-frontend/src/SqlLab/types.ts index f30e16371a57e..86c448d255141 100644 --- a/superset-frontend/src/SqlLab/types.ts +++ b/superset-frontend/src/SqlLab/types.ts @@ -17,80 +17,11 @@ * under the License. */ import { SupersetError } from 'src/components/ErrorMessage/types'; -import { CtasEnum } from 'src/SqlLab/actions/sqlLab'; import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes'; import { ToastType } from 'src/components/MessageToasts/types'; -import { DatasourceType } from '@superset-ui/core'; +import { Query } from '@superset-ui/core'; import { ExploreRootState } from 'src/explore/types'; -// same as superset.result_set.ResultSetColumnType -export type Column = { - name: string; - type: string | null; - is_dttm: boolean; -}; - -export type QueryState = - | 'stopped' - | 'failed' - | 'pending' - | 'running' - | 'scheduled' - | 'success' - | 'fetching' - | 'timed_out'; - -export type Query = { - cached: boolean; - ctas: boolean; - ctas_method?: keyof typeof CtasEnum; - dbId: number; - errors?: SupersetError[]; - errorMessage: string | null; - extra: { - progress: string | null; - }; - id: string; - isDataPreview: boolean; - link?: string; - progress: number; - results: { - displayLimitReached: boolean; - columns: Column[]; - data: Record[]; - expanded_columns: Column[]; - selected_columns: Column[]; - query: { limit: number }; - }; - resultsKey: string | null; - schema?: string; - sql: string; - sqlEditorId: string; - state: QueryState; - tab: string | null; - tempSchema: string | null; - tempTable: string; - trackingUrl: string | null; - templateParams: any; - rows: number; - queryLimit: number; - limitingFactor: string; - endDttm: number; - duration: string; - startDttm: number; - time: Record; - user: Record; - userId: number; - db: Record; - started: string; - querylink: Record; - queryId: number; - executedSql: string; - output: string | Record; - actions: Record; - type: DatasourceType.Query; -}; - export interface QueryEditor { dbId?: number; title: string; diff --git a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx index 32b69198364cd..2bd4b3b7618d8 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx @@ -17,7 +17,7 @@ * under the License. */ import React, { useEffect, useMemo, useRef, useState } from 'react'; -import { css, styled, t, DatasourceType } from '@superset-ui/core'; +import { css, styled, t, DatasourceType, Query } from '@superset-ui/core'; import { ControlConfig, Dataset, @@ -33,7 +33,6 @@ import { FAST_DEBOUNCE } from 'src/constants'; import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags'; import { ExploreActions } from 'src/explore/actions/exploreActions'; import Control from 'src/explore/components/Control'; -import { Query } from 'src/SqlLab/types'; import DatasourcePanelDragOption from './DatasourcePanelDragOption'; import { DndItemType } from '../DndItemType'; import { StyledColumnOption, StyledMetricOption } from '../optionRenderers'; From 9b14992640a836472359ed57d26e16635b394347 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Thu, 19 May 2022 07:02:17 -0500 Subject: [PATCH 29/57] Deconstruct query props in ResultSet --- .../src/SqlLab/components/ResultSet/index.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx index a04dd2c9ce5cd..fa290d1a86822 100644 --- a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx +++ b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx @@ -218,6 +218,7 @@ export default class ResultSet extends React.PureComponent< const { columns } = this.props.query.results; // Added compute logic to stop user from being able to Save & Explore const { showSaveDatasetModal } = this.state; + const { query } = this.props; return ( @@ -229,7 +230,7 @@ export default class ResultSet extends React.PureComponent< modalDescription={t( 'Save this query as a virtual dataset to continue exploring', )} - datasource={this.props.query} + datasource={query} /> {this.props.visualize && @@ -240,10 +241,7 @@ export default class ResultSet extends React.PureComponent< /> )} {this.props.csv && ( - )} From 8558460f8cfb881042a304019da577f2f6cadb87 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Thu, 19 May 2022 07:12:16 -0500 Subject: [PATCH 30/57] Fix union type in /legacy-plugin-chart-heatmap/src/controlPanel --- .../plugins/legacy-plugin-chart-heatmap/src/controlPanel.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/superset-frontend/plugins/legacy-plugin-chart-heatmap/src/controlPanel.ts b/superset-frontend/plugins/legacy-plugin-chart-heatmap/src/controlPanel.ts index ab3eb2a59fa11..558f746667da0 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-heatmap/src/controlPanel.ts +++ b/superset-frontend/plugins/legacy-plugin-chart-heatmap/src/controlPanel.ts @@ -30,6 +30,7 @@ import { formatSelectOptionsForRange, sections, dndEntity, + Dataset, } from '@superset-ui/chart-controls'; const sortAxisChoices = [ @@ -44,7 +45,7 @@ const allColumns = { default: null, description: t('Columns to display'), mapStateToProps: (state: ControlPanelState) => ({ - choices: columnChoices(state.datasource), + choices: columnChoices(state.datasource as Dataset), }), validators: [validateNonEmpty], }; From c1fb8a6250d30f33f37d18c2a4338e1026542e9f Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Thu, 19 May 2022 08:56:36 -0500 Subject: [PATCH 31/57] Change SaveDatasetModal tests to RTL --- .../SaveDatasetModal.test.tsx | 89 +++++++++++-------- 1 file changed, 54 insertions(+), 35 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx b/superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx index cab55599941a4..6ccfb7a6781b5 100644 --- a/superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx +++ b/superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx @@ -17,44 +17,63 @@ * under the License. */ import React from 'react'; -import { shallow } from 'enzyme'; -import { Radio } from 'src/components/Radio'; -import { AutoComplete } from 'src/components'; -import { Input } from 'src/components/Input'; +import { Query } from '@superset-ui/core'; import { SaveDatasetModal } from 'src/SqlLab/components/SaveDatasetModal'; +import { render, screen } from 'spec/helpers/testing-library'; +import { query } from 'src/SqlLab/fixtures'; -describe('SaveDatasetModal', () => { - const mockedProps = { - visible: false, - onOk: () => {}, - onHide: () => {}, - handleDatasetNameChange: () => {}, - handleSaveDatasetRadioBtnState: () => {}, - saveDatasetRadioBtnState: 1, - handleOverwriteCancel: () => {}, - handleOverwriteDataset: () => {}, - handleOverwriteDatasetOption: () => {}, - defaultCreateDatasetValue: 'someDatasets', - shouldOverwriteDataset: false, - userDatasetOptions: [], - disableSaveAndExploreBtn: false, - handleSaveDatasetModalSearch: () => Promise, - filterAutocompleteOption: () => false, - onChangeAutoComplete: () => {}, - }; - it('renders a radio group btn', () => { - // @ts-ignore - const wrapper = shallow(); - expect(wrapper.find(Radio.Group)).toExist(); +const mockedProps = { + visible: true, + onHide: () => {}, + buttonTextOnSave: 'Save', + buttonTextOnOverwrite: 'Overwrite', + datasource: query as unknown as Query, +}; + +describe('SaveDatasetModal RTL', () => { + it('renders a "Save as new" field', () => { + render(, { useRedux: true }); + + const saveRadioBtn = screen.getByRole('radio', { + name: /save as new unimportant 05\/19\/2022/i, + }); + const fieldLabel = screen.getByText(/save as new/i); + const inputField = screen.getByRole('textbox'); + const inputFieldText = screen.getByDisplayValue( + /unimportant 05\/19\/2022/i, + ); + + expect(saveRadioBtn).toBeVisible(); + expect(fieldLabel).toBeVisible(); + expect(inputField).toBeVisible(); + expect(inputFieldText).toBeVisible(); }); - it('renders a autocomplete', () => { - // @ts-ignore - const wrapper = shallow(); - expect(wrapper.find(AutoComplete)).toExist(); + + it('renders an "Overwrite existing" field', () => { + render(, { useRedux: true }); + + const overwriteRadioBtn = screen.getByRole('radio', { + name: /overwrite existing select or type dataset name/i, + }); + const fieldLabel = screen.getByText(/overwrite existing/i); + const inputField = screen.getByRole('combobox'); + const placeholderText = screen.getByText(/select or type dataset name/i); + + expect(overwriteRadioBtn).toBeVisible(); + expect(fieldLabel).toBeVisible(); + expect(inputField).toBeVisible(); + expect(placeholderText).toBeVisible(); }); - it('renders an input form', () => { - // @ts-ignore - const wrapper = shallow(); - expect(wrapper.find(Input)).toExist(); + + it('renders a save button', () => { + render(, { useRedux: true }); + + expect(screen.getByRole('button', { name: /save/i })).toBeVisible(); + }); + + it('renders a close button', () => { + render(, { useRedux: true }); + + expect(screen.getByRole('button', { name: /close/i })).toBeVisible(); }); }); From 9d225c4af66c3c8f1b7dae1fc5ea60652f1e5025 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Thu, 19 May 2022 11:27:16 -0500 Subject: [PATCH 32/57] Cleaned datasourceTypeCheck --- .../components/DatasourcePanel/index.tsx | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx index 2bd4b3b7618d8..71af5bdef5932 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx @@ -306,16 +306,11 @@ export default function DataSourcePanel({ return true; }; - const datasourceTypeCheck = () => { - if ( - datasource.type === DatasourceType.Dataset || - datasource.type === DatasourceType.SlTable || - datasource.type === DatasourceType.SavedQuery || - datasource.type === DatasourceType.Query - ) - return true; - return false; - }; + const datasourceTypeCheck = + datasource.type === DatasourceType.Dataset || + datasource.type === DatasourceType.SlTable || + datasource.type === DatasourceType.SavedQuery || + datasource.type === DatasourceType.Query; const mainBody = useMemo( () => ( @@ -331,7 +326,7 @@ export default function DataSourcePanel({ placeholder={t('Search Metrics & Columns')} />
- {datasourceTypeCheck() && showInfoboxCheck() && ( + {datasourceTypeCheck && showInfoboxCheck() && ( Date: Thu, 19 May 2022 19:14:59 -0500 Subject: [PATCH 33/57] Fix infobox styling --- .../src/explore/components/DatasourcePanel/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx index 71af5bdef5932..ba6f61771154a 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx @@ -156,7 +156,7 @@ const SectionHeader = styled.span` const StyledInfoboxWrapper = styled.div` ${({ theme }) => css` - margin: 0 ${theme.gridUnit * 2.5}; + margin: 0 ${theme.gridUnit * 2.5}px; span { text-decoration: underline; From a0c771bd9b6f510ea9cba48fddd6d0908759639d Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Thu, 19 May 2022 20:19:10 -0500 Subject: [PATCH 34/57] Fix SaveDatasetModal test --- .../components/SaveDatasetModal/SaveDatasetModal.test.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx b/superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx index 6ccfb7a6781b5..be7be32c961ff 100644 --- a/superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx +++ b/superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx @@ -35,13 +35,11 @@ describe('SaveDatasetModal RTL', () => { render(, { useRedux: true }); const saveRadioBtn = screen.getByRole('radio', { - name: /save as new unimportant 05\/19\/2022/i, + name: /save as new unimportant/i, }); const fieldLabel = screen.getByText(/save as new/i); const inputField = screen.getByRole('textbox'); - const inputFieldText = screen.getByDisplayValue( - /unimportant 05\/19\/2022/i, - ); + const inputFieldText = screen.getByDisplayValue(/unimportant/i); expect(saveRadioBtn).toBeVisible(); expect(fieldLabel).toBeVisible(); From bc384387345c071dcb853b1df6fde02200653902 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Fri, 20 May 2022 07:58:35 -0500 Subject: [PATCH 35/57] Fix query fixture in sqllab and Query type in SaveDatasetModal test --- .../superset-ui-core/src/query/types/Query.ts | 2 +- .../SaveDatasetModal.test.tsx | 2 +- superset-frontend/src/SqlLab/fixtures.ts | 64 ++++++++++++++++++- 3 files changed, 64 insertions(+), 4 deletions(-) diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts b/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts index 83104be64af4e..8436d50cfd47e 100644 --- a/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts +++ b/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts @@ -293,7 +293,7 @@ export type Query = { state: QueryState; tab: string | null; tempSchema: string | null; - tempTable: string; + tempTable: string | null; trackingUrl: string | null; templateParams: any; rows: number; diff --git a/superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx b/superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx index be7be32c961ff..92f56c3418d96 100644 --- a/superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx +++ b/superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx @@ -27,7 +27,7 @@ const mockedProps = { onHide: () => {}, buttonTextOnSave: 'Save', buttonTextOnOverwrite: 'Overwrite', - datasource: query as unknown as Query, + datasource: query as Query, }; describe('SaveDatasetModal RTL', () => { diff --git a/superset-frontend/src/SqlLab/fixtures.ts b/superset-frontend/src/SqlLab/fixtures.ts index 5b12ee29213ee..9f8d39abec676 100644 --- a/superset-frontend/src/SqlLab/fixtures.ts +++ b/superset-frontend/src/SqlLab/fixtures.ts @@ -17,6 +17,7 @@ * under the License. */ import sinon from 'sinon'; +import { DatasourceType, Query } from '@superset-ui/core'; import * as actions from 'src/SqlLab/actions/sqlLab'; import { ColumnKeyTypeType } from 'src/SqlLab/components/ColumnElement'; @@ -561,14 +562,73 @@ export const initialState = { }, }; -export const query = { +export const query: Query = { id: 'clientId2353', dbId: 1, sql: 'SELECT * FROM something', sqlEditorId: defaultQueryEditor.id, tab: 'unimportant', tempTable: null, - runAsync: false, + // runAsync: false, ctas: false, cached: false, + errorMessage: null, + extra: { progress: null }, + isDataPreview: false, + progress: 0, + results: { + columns: [ + { + is_dttm: true, + name: 'ds', + type: 'STRING', + }, + { + is_dttm: false, + name: 'gender', + type: 'STRING', + }, + ], + selected_columns: [ + { + is_dttm: true, + name: 'ds', + type: 'STRING', + }, + { + is_dttm: false, + name: 'gender', + type: 'STRING', + }, + ], + expanded_columns: [], + query: { limit: 0 }, + displayLimitReached: false, + data: [ + { col1: 0, col2: 1 }, + { col1: 2, col2: 3 }, + ], + }, + resultsKey: null, + state: 'success', + tempSchema: null, + trackingUrl: null, + templateParams: null, + rows: 42, + queryLimit: 100, + limitingFactor: '', + endDttm: 1476910579693, + duration: '', + startDttm: 1476910566092.96, + time: {}, + user: {}, + userId: 1, + db: {}, + started: '', + querylink: {}, + queryId: 1, + executedSql: '', + output: '', + actions: {}, + type: DatasourceType.Query, }; From 91f06ea5b4fb1725dc8cb6e323a9f80f4e18f8f6 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Fri, 20 May 2022 08:11:34 -0500 Subject: [PATCH 36/57] Fix Query type and make test query fixture --- .../superset-ui-core/src/query/types/Query.ts | 2 +- .../SaveDatasetModal/SaveDatasetModal.test.tsx | 4 ++-- superset-frontend/src/SqlLab/fixtures.ts | 17 ++++++++++++++--- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts b/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts index 8436d50cfd47e..83104be64af4e 100644 --- a/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts +++ b/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts @@ -293,7 +293,7 @@ export type Query = { state: QueryState; tab: string | null; tempSchema: string | null; - tempTable: string | null; + tempTable: string; trackingUrl: string | null; templateParams: any; rows: number; diff --git a/superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx b/superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx index 92f56c3418d96..3ae0934c8ab5b 100644 --- a/superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx +++ b/superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx @@ -20,14 +20,14 @@ import React from 'react'; import { Query } from '@superset-ui/core'; import { SaveDatasetModal } from 'src/SqlLab/components/SaveDatasetModal'; import { render, screen } from 'spec/helpers/testing-library'; -import { query } from 'src/SqlLab/fixtures'; +import { testQuery } from 'src/SqlLab/fixtures'; const mockedProps = { visible: true, onHide: () => {}, buttonTextOnSave: 'Save', buttonTextOnOverwrite: 'Overwrite', - datasource: query as Query, + datasource: testQuery as Query, }; describe('SaveDatasetModal RTL', () => { diff --git a/superset-frontend/src/SqlLab/fixtures.ts b/superset-frontend/src/SqlLab/fixtures.ts index 9f8d39abec676..ce82f7d08aa91 100644 --- a/superset-frontend/src/SqlLab/fixtures.ts +++ b/superset-frontend/src/SqlLab/fixtures.ts @@ -17,9 +17,9 @@ * under the License. */ import sinon from 'sinon'; -import { DatasourceType, Query } from '@superset-ui/core'; import * as actions from 'src/SqlLab/actions/sqlLab'; import { ColumnKeyTypeType } from 'src/SqlLab/components/ColumnElement'; +import { DatasourceType, Query } from '@superset-ui/core'; export const mockedActions = sinon.stub({ ...actions }); @@ -562,14 +562,25 @@ export const initialState = { }, }; -export const query: Query = { +export const query = { id: 'clientId2353', dbId: 1, sql: 'SELECT * FROM something', sqlEditorId: defaultQueryEditor.id, tab: 'unimportant', tempTable: null, - // runAsync: false, + runAsync: false, + ctas: false, + cached: false, +}; + +export const testQuery: Query = { + id: 'clientId2353', + dbId: 1, + sql: 'SELECT * FROM something', + sqlEditorId: defaultQueryEditor.id, + tab: 'unimportant', + tempTable: '', ctas: false, cached: false, errorMessage: null, From 55bb3797ef7110994c71b24f4695653f9181c2dd Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Thu, 26 May 2022 14:08:51 -0500 Subject: [PATCH 37/57] Added columns to Query type, separated results property, created QueryResponse union type, and fixed all types affected --- .../superset-ui-chart-controls/src/types.ts | 4 ++-- .../superset-ui-core/src/query/types/Query.ts | 22 +++++++++++------- .../src/controlPanel.tsx | 2 +- .../src/plugin/controls/columns.tsx | 13 +++++++---- .../src/plugin/controls/metrics.tsx | 9 ++++++-- .../plugin-chart-table/src/controlPanel.tsx | 23 +++++++++++-------- .../SqlLab/components/QueryTable/index.tsx | 12 +++++----- .../src/SqlLab/components/ResultSet/index.tsx | 10 ++++---- .../SaveDatasetModal.test.tsx | 4 ++-- .../components/SaveDatasetModal/index.tsx | 6 ++--- 10 files changed, 62 insertions(+), 43 deletions(-) diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/types.ts b/superset-frontend/packages/superset-ui-chart-controls/src/types.ts index baa66f0310a32..3b7fe69dcb738 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/types.ts +++ b/superset-frontend/packages/superset-ui-chart-controls/src/types.ts @@ -25,7 +25,7 @@ import type { JsonValue, Metric, QueryFormData, - Query, + QueryResponse, } from '@superset-ui/core'; import { sharedControls } from './shared-controls'; import sharedControlComponents from './shared-controls/components'; @@ -70,7 +70,7 @@ export interface Dataset { export interface ControlPanelState { form_data: QueryFormData; - datasource: Dataset | Query | null; + datasource: Dataset | QueryResponse | null; controls: ControlStateMapping; } diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts b/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts index 83104be64af4e..e863e0afa68e6 100644 --- a/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts +++ b/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts @@ -278,14 +278,6 @@ export type Query = { isDataPreview: boolean; link?: string; progress: number; - results: { - displayLimitReached: boolean; - columns: QueryColumn[]; - data: Record[]; - expanded_columns: QueryColumn[]; - selected_columns: QueryColumn[]; - query: { limit: number }; - }; resultsKey: string | null; schema?: string; sql: string; @@ -313,6 +305,20 @@ export type Query = { output: string | Record; actions: Record; type: DatasourceType.Query; + columns: QueryColumn[]; }; +export type QueryResults = { + results: { + displayLimitReached: boolean; + columns: QueryColumn[]; + data: Record[]; + expanded_columns: QueryColumn[]; + selected_columns: QueryColumn[]; + query: { limit: number }; + }; +}; + +export type QueryResponse = Query & QueryResults; + export default {}; diff --git a/superset-frontend/plugins/legacy-plugin-chart-event-flow/src/controlPanel.tsx b/superset-frontend/plugins/legacy-plugin-chart-event-flow/src/controlPanel.tsx index 7c6cf1fd53aaa..f4871ff54849c 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-event-flow/src/controlPanel.tsx +++ b/superset-frontend/plugins/legacy-plugin-chart-event-flow/src/controlPanel.tsx @@ -110,7 +110,7 @@ const config: ControlPanelConfig = { valueKey: 'column_name', allowAll: true, mapStateToProps: state => ({ - options: (state.datasource as Dataset)?.columns || [], + options: state.datasource?.columns || [], }), commaChoosesOption: false, freeForm: true, diff --git a/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/columns.tsx b/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/columns.tsx index 24fd182c557d6..ca628a0d122f5 100644 --- a/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/columns.tsx +++ b/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/columns.tsx @@ -22,6 +22,7 @@ import { ExtraControlProps, sharedControls, Dataset, + ColumnMeta, } from '@superset-ui/chart-controls'; import { ensureIsArray, @@ -45,7 +46,7 @@ const allColumns: typeof sharedControls.groupby = { valueRenderer: c => , valueKey: 'column_name', mapStateToProps: ({ datasource, controls }, controlState) => ({ - options: (datasource as Dataset)?.columns || [], + options: datasource?.columns || [], queryMode: getQueryMode(controls), externalValidationErrors: isRawMode({ controls }) && ensureIsArray(controlState.value).length === 0 @@ -64,10 +65,12 @@ const dndAllColumns: typeof sharedControls.groupby = { mapStateToProps({ datasource, controls }, controlState) { const newState: ExtraControlProps = {}; if (datasource) { - const options = (datasource as Dataset).columns; - newState.options = Object.fromEntries( - options.map(option => [option.column_name, option]), - ); + if (datasource?.columns?.hasOwnProperty('filterable')) { + const options = (datasource as Dataset).columns; + newState.options = Object.fromEntries( + options.map((option: ColumnMeta) => [option.column_name, option]), + ); + } else newState.options = datasource.columns; } newState.queryMode = getQueryMode(controls); newState.externalValidationErrors = diff --git a/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/metrics.tsx b/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/metrics.tsx index ea006ff62c824..1e9f431e21a21 100644 --- a/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/metrics.tsx +++ b/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/metrics.tsx @@ -22,6 +22,7 @@ import { ControlState, sharedControls, Dataset, + ColumnMeta, } from '@superset-ui/chart-controls'; import { FeatureFlag, isFeatureEnabled, t } from '@superset-ui/core'; import { getQueryMode, isAggMode, validateAggControlValues } from './shared'; @@ -36,7 +37,7 @@ const percentMetrics: typeof sharedControls.metrics = { visibility: isAggMode, resetOnHide: false, mapStateToProps: ({ datasource, controls }, controlState) => ({ - columns: (datasource as Dataset)?.columns || [], + columns: datasource?.columns || [], savedMetrics: (datasource as Dataset)?.metrics || [], datasource, datasourceType: (datasource as Dataset)?.type, @@ -75,7 +76,11 @@ export const metricsControlSetItem: ControlSetItem = { { controls, datasource, form_data }: ControlPanelState, controlState: ControlState, ) => ({ - columns: (datasource as Dataset)?.columns.filter(c => c.filterable) || [], + columns: datasource?.columns?.hasOwnProperty('filterable') + ? (datasource as Dataset)?.columns?.filter( + (c: ColumnMeta) => c.filterable, + ) + : datasource?.columns, savedMetrics: (datasource as Dataset)?.metrics || [], // current active adhoc metrics selectedMetrics: diff --git a/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx index 5f2d65ce8fd3e..e977933c7990a 100644 --- a/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx @@ -45,6 +45,7 @@ import { ControlState, emitFilterControl, Dataset, + ColumnMeta, } from '@superset-ui/chart-controls'; import i18n from './i18n'; @@ -110,7 +111,7 @@ const all_columns: typeof sharedControls.groupby = { valueRenderer: c => , valueKey: 'column_name', mapStateToProps: ({ datasource, controls }, controlState) => ({ - options: (datasource as Dataset)?.columns || [], + options: datasource?.columns || [], queryMode: getQueryMode(controls), externalValidationErrors: isRawMode({ controls }) && ensureIsArray(controlState.value).length === 0 @@ -129,10 +130,12 @@ const dnd_all_columns: typeof sharedControls.groupby = { mapStateToProps({ datasource, controls }, controlState) { const newState: ExtraControlProps = {}; if (datasource) { - const options = (datasource as Dataset).columns; - newState.options = Object.fromEntries( - options.map(option => [option.column_name, option]), - ); + if (datasource?.columns?.hasOwnProperty('filterable')) { + const options = (datasource as Dataset).columns; + newState.options = Object.fromEntries( + options.map((option: ColumnMeta) => [option.column_name, option]), + ); + } else newState.options = datasource.columns; } newState.queryMode = getQueryMode(controls); newState.externalValidationErrors = @@ -155,7 +158,7 @@ const percent_metrics: typeof sharedControls.metrics = { visibility: isAggMode, resetOnHide: false, mapStateToProps: ({ datasource, controls }, controlState) => ({ - columns: (datasource as Dataset)?.columns || [], + columns: datasource?.columns || [], savedMetrics: (datasource as Dataset)?.metrics || [], datasource, datasourceType: (datasource as Dataset)?.type, @@ -230,9 +233,11 @@ const config: ControlPanelConfig = { { controls, datasource, form_data }: ControlPanelState, controlState: ControlState, ) => ({ - columns: - (datasource as Dataset)?.columns.filter(c => c.filterable) || - [], + columns: datasource?.columns?.hasOwnProperty('filterable') + ? (datasource as Dataset)?.columns?.filter( + (c: ColumnMeta) => c.filterable, + ) + : datasource?.columns, savedMetrics: (datasource as Dataset)?.metrics || [], // current active adhoc metrics selectedMetrics: diff --git a/superset-frontend/src/SqlLab/components/QueryTable/index.tsx b/superset-frontend/src/SqlLab/components/QueryTable/index.tsx index 1c7ea57b3c0d5..54edb7f97e8d3 100644 --- a/superset-frontend/src/SqlLab/components/QueryTable/index.tsx +++ b/superset-frontend/src/SqlLab/components/QueryTable/index.tsx @@ -21,7 +21,7 @@ import moment from 'moment'; import Card from 'src/components/Card'; import ProgressBar from 'src/components/ProgressBar'; import Label from 'src/components/Label'; -import { t, useTheme, Query } from '@superset-ui/core'; +import { t, useTheme, QueryResponse } from '@superset-ui/core'; import { useSelector } from 'react-redux'; import TableView from 'src/components/TableView'; import Button from 'src/components/Button'; @@ -36,7 +36,7 @@ import HighlightedSql from '../HighlightedSql'; import { StaticPosition, verticalAlign, StyledTooltip } from './styles'; interface QueryTableQuery - extends Omit { + extends Omit { state?: Record; sql?: Record; progress?: Record; @@ -52,7 +52,7 @@ interface QueryTableProps { clearQueryResults: Function; removeQuery: Function; }; - queries?: Query[]; + queries?: QueryResponse[]; onUserClicked?: Function; onDbClicked?: Function; displayLimit: number; @@ -102,15 +102,15 @@ const QueryTable = ({ } = actions; const data = useMemo(() => { - const restoreSql = (query: Query) => { + const restoreSql = (query: QueryResponse) => { queryEditorSetAndSaveSql({ id: query.sqlEditorId }, query.sql); }; - const openQueryInNewTab = (query: Query) => { + const openQueryInNewTab = (query: QueryResponse) => { cloneQueryToNewTab(query, true); }; - const openAsyncResults = (query: Query, displayLimit: number) => { + const openAsyncResults = (query: QueryResponse, displayLimit: number) => { fetchQueryResults(query, displayLimit); }; diff --git a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx index fa290d1a86822..339303ba57928 100644 --- a/superset-frontend/src/SqlLab/components/ResultSet/index.tsx +++ b/superset-frontend/src/SqlLab/components/ResultSet/index.tsx @@ -21,7 +21,7 @@ import ButtonGroup from 'src/components/ButtonGroup'; import Alert from 'src/components/Alert'; import Button from 'src/components/Button'; import shortid from 'shortid'; -import { styled, t, Query } from '@superset-ui/core'; +import { styled, t, QueryResponse } from '@superset-ui/core'; import ErrorMessageWithStackTrace from 'src/components/ErrorMessage/ErrorMessageWithStackTrace'; import { SaveDatasetModal } from 'src/SqlLab/components/SaveDatasetModal'; import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes'; @@ -55,7 +55,7 @@ interface ResultSetProps { database?: Record; displayLimit: number; height: number; - query: Query; + query: QueryResponse; search?: boolean; showSql?: boolean; visualize?: boolean; @@ -166,7 +166,7 @@ export default class ResultSet extends React.PureComponent< } }; - clearQueryResults(query: Query) { + clearQueryResults(query: QueryResponse) { this.props.actions.clearQueryResults(query); } @@ -191,11 +191,11 @@ export default class ResultSet extends React.PureComponent< this.setState({ searchText: event.target.value }); } - fetchResults(query: Query) { + fetchResults(query: QueryResponse) { this.props.actions.fetchQueryResults(query, this.props.displayLimit); } - reFetchQueryResults(query: Query) { + reFetchQueryResults(query: QueryResponse) { this.props.actions.reFetchQueryResults(query); } diff --git a/superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx b/superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx index 3ae0934c8ab5b..c895867ac7d32 100644 --- a/superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx +++ b/superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx @@ -17,7 +17,7 @@ * under the License. */ import React from 'react'; -import { Query } from '@superset-ui/core'; +import { QueryResponse } from '@superset-ui/core'; import { SaveDatasetModal } from 'src/SqlLab/components/SaveDatasetModal'; import { render, screen } from 'spec/helpers/testing-library'; import { testQuery } from 'src/SqlLab/fixtures'; @@ -27,7 +27,7 @@ const mockedProps = { onHide: () => {}, buttonTextOnSave: 'Save', buttonTextOnOverwrite: 'Overwrite', - datasource: testQuery as Query, + datasource: testQuery as QueryResponse, }; describe('SaveDatasetModal RTL', () => { diff --git a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx index 56cd978067f09..1ecdce9a1dc6b 100644 --- a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx +++ b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx @@ -30,7 +30,7 @@ import { makeApi, JsonResponse, JsonObject, - Query, + QueryResponse, } from '@superset-ui/core'; import { useSelector, useDispatch } from 'react-redux'; import moment from 'moment'; @@ -49,7 +49,7 @@ import { import { Dataset } from '@superset-ui/chart-controls'; import { exploreChart } from 'src/explore/exploreUtils'; -type ExploreDatasource = Dataset | Query; +type ExploreDatasource = Dataset | QueryResponse; interface SaveDatasetModalProps { visible: boolean; @@ -117,7 +117,7 @@ export const SaveDatasetModal: FunctionComponent = ({ modalDescription, datasource, }) => { - const query = datasource as Query; + const query = datasource as QueryResponse; const getDefaultDatasetName = () => `${query.tab} ${moment().format('MM/DD/YYYY HH:mm:ss')}`; const [datasetName, setDatasetName] = useState(getDefaultDatasetName()); From dab90b48783bec098d9851cf21786d961338e120 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Thu, 26 May 2022 14:20:19 -0500 Subject: [PATCH 38/57] Fixed a couple missed broken types --- .../SqlLab/components/QueryHistory/index.tsx | 4 +-- .../SqlLab/components/QuerySearch/index.tsx | 4 +-- superset-frontend/src/SqlLab/fixtures.ts | 34 +------------------ 3 files changed, 5 insertions(+), 37 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/QueryHistory/index.tsx b/superset-frontend/src/SqlLab/components/QueryHistory/index.tsx index 56f7f42d8f900..86f28069209da 100644 --- a/superset-frontend/src/SqlLab/components/QueryHistory/index.tsx +++ b/superset-frontend/src/SqlLab/components/QueryHistory/index.tsx @@ -18,11 +18,11 @@ */ import React from 'react'; import { EmptyStateMedium } from 'src/components/EmptyState'; -import { t, styled, Query } from '@superset-ui/core'; +import { t, styled, QueryResponse } from '@superset-ui/core'; import QueryTable from 'src/SqlLab/components/QueryTable'; interface QueryHistoryProps { - queries: Query[]; + queries: QueryResponse[]; actions: { queryEditorSetAndSaveSql: Function; cloneQueryToNewTab: Function; diff --git a/superset-frontend/src/SqlLab/components/QuerySearch/index.tsx b/superset-frontend/src/SqlLab/components/QuerySearch/index.tsx index 17bdbe1cb1180..635603e255f14 100644 --- a/superset-frontend/src/SqlLab/components/QuerySearch/index.tsx +++ b/superset-frontend/src/SqlLab/components/QuerySearch/index.tsx @@ -19,7 +19,7 @@ import React, { useState, useEffect } from 'react'; import Button from 'src/components/Button'; import Select from 'src/components/Select'; -import { styled, t, SupersetClient, Query } from '@superset-ui/core'; +import { styled, t, SupersetClient, QueryResponse } from '@superset-ui/core'; import { debounce } from 'lodash'; import Loading from 'src/components/Loading'; import { @@ -84,7 +84,7 @@ function QuerySearch({ actions, displayLimit }: QuerySearchProps) { const [from, setFrom] = useState('28 days ago'); const [to, setTo] = useState('now'); const [status, setStatus] = useState('success'); - const [queriesArray, setQueriesArray] = useState([]); + const [queriesArray, setQueriesArray] = useState([]); const [queriesLoading, setQueriesLoading] = useState(true); const getTimeFromSelection = (selection: string) => { diff --git a/superset-frontend/src/SqlLab/fixtures.ts b/superset-frontend/src/SqlLab/fixtures.ts index ce82f7d08aa91..97cefe0329ffb 100644 --- a/superset-frontend/src/SqlLab/fixtures.ts +++ b/superset-frontend/src/SqlLab/fixtures.ts @@ -587,39 +587,6 @@ export const testQuery: Query = { extra: { progress: null }, isDataPreview: false, progress: 0, - results: { - columns: [ - { - is_dttm: true, - name: 'ds', - type: 'STRING', - }, - { - is_dttm: false, - name: 'gender', - type: 'STRING', - }, - ], - selected_columns: [ - { - is_dttm: true, - name: 'ds', - type: 'STRING', - }, - { - is_dttm: false, - name: 'gender', - type: 'STRING', - }, - ], - expanded_columns: [], - query: { limit: 0 }, - displayLimitReached: false, - data: [ - { col1: 0, col2: 1 }, - { col1: 2, col2: 3 }, - ], - }, resultsKey: null, state: 'success', tempSchema: null, @@ -642,4 +609,5 @@ export const testQuery: Query = { output: '', actions: {}, type: DatasourceType.Query, + columns: [], }; From c56045611c1128c83233e8a9712bcb6a1d233502 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Thu, 26 May 2022 14:51:00 -0500 Subject: [PATCH 39/57] Added ExploreDatasource to SqlLab type file --- .../src/SqlLab/components/SaveDatasetModal/index.tsx | 4 +--- superset-frontend/src/SqlLab/types.ts | 5 ++++- .../src/explore/components/DatasourcePanel/index.tsx | 3 ++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx index 1ecdce9a1dc6b..94b67a6fb309f 100644 --- a/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx +++ b/superset-frontend/src/SqlLab/components/SaveDatasetModal/index.tsx @@ -45,12 +45,10 @@ import { DatasetOptionAutocomplete, SqlLabExploreRootState, getInitialState, + ExploreDatasource, } from 'src/SqlLab/types'; -import { Dataset } from '@superset-ui/chart-controls'; import { exploreChart } from 'src/explore/exploreUtils'; -type ExploreDatasource = Dataset | QueryResponse; - interface SaveDatasetModalProps { visible: boolean; onHide: () => void; diff --git a/superset-frontend/src/SqlLab/types.ts b/superset-frontend/src/SqlLab/types.ts index 86c448d255141..fb3993fe84f6b 100644 --- a/superset-frontend/src/SqlLab/types.ts +++ b/superset-frontend/src/SqlLab/types.ts @@ -19,9 +19,12 @@ import { SupersetError } from 'src/components/ErrorMessage/types'; import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes'; import { ToastType } from 'src/components/MessageToasts/types'; -import { Query } from '@superset-ui/core'; +import { Dataset } from '@superset-ui/chart-controls'; +import { Query, QueryResponse } from '@superset-ui/core'; import { ExploreRootState } from 'src/explore/types'; +export type ExploreDatasource = Dataset | QueryResponse; + export interface QueryEditor { dbId?: number; title: string; diff --git a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx index ba6f61771154a..a2945192cb69e 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx @@ -33,12 +33,13 @@ import { FAST_DEBOUNCE } from 'src/constants'; import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags'; import { ExploreActions } from 'src/explore/actions/exploreActions'; import Control from 'src/explore/components/Control'; +import { ExploreDatasource } from 'src/SqlLab/types'; import DatasourcePanelDragOption from './DatasourcePanelDragOption'; import { DndItemType } from '../DndItemType'; import { StyledColumnOption, StyledMetricOption } from '../optionRenderers'; interface DatasourceControl extends ControlConfig { - datasource?: Dataset | Query; + datasource?: ExploreDatasource; } export interface Props { From 30f4508273f0f86665a6e19acc9c23a803027a01 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Thu, 26 May 2022 15:04:53 -0500 Subject: [PATCH 40/57] Removed unneeded Query import from DatasourcePanel --- .../src/explore/components/DatasourcePanel/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx index a2945192cb69e..e786f4b30f30b 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx @@ -17,7 +17,7 @@ * under the License. */ import React, { useEffect, useMemo, useRef, useState } from 'react'; -import { css, styled, t, DatasourceType, Query } from '@superset-ui/core'; +import { css, styled, t, DatasourceType } from '@superset-ui/core'; import { ControlConfig, Dataset, From 6ee52eae963ae83a36ed3c98d86ae99f83967c34 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Wed, 1 Jun 2022 22:48:39 -0500 Subject: [PATCH 41/57] Address PR comments --- .../src/constants.ts | 7 ++ .../src/shared-controls/dndControls.tsx | 102 ++++++++++-------- .../src/shared-controls/index.tsx | 91 +++++++++------- .../src/utils/columnChoices.ts | 22 ++-- .../src/controlPanel.tsx | 3 +- .../src/controlPanel.ts | 3 +- .../src/controlPanel.ts | 3 +- .../src/controlPanel.ts | 9 +- .../src/plugin/controls/metrics.tsx | 11 +- .../src/plugin/controls/orderBy.tsx | 4 +- .../src/plugin/controlPanel.tsx | 7 +- .../plugin-chart-table/src/controlPanel.tsx | 36 ++++--- superset-frontend/src/explore/fixtures.tsx | 3 +- 13 files changed, 177 insertions(+), 124 deletions(-) diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/constants.ts b/superset-frontend/packages/superset-ui-chart-controls/src/constants.ts index 5e16956c60e6b..39988f87a4d5b 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/constants.ts +++ b/superset-frontend/packages/superset-ui-chart-controls/src/constants.ts @@ -45,3 +45,10 @@ export const QueryModeLabel = { [QueryMode.aggregate]: t('Aggregate'), [QueryMode.raw]: t('Raw records'), }; + +export const DEFAULT_METRICS = [ + { + metric_name: 'COUNT(*)', + expression: 'COUNT(*)', + }, +]; diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx index 51880fa88a3f7..597ac4b11a918 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx +++ b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx @@ -20,11 +20,16 @@ import { FeatureFlag, isFeatureEnabled, + QueryResponse, t, validateNonEmpty, } from '@superset-ui/core'; import { ExtraControlProps, SharedControlConfig, Dataset } from '../types'; -import { TIME_COLUMN_OPTION, TIME_FILTER_LABELS } from '../constants'; +import { + TIME_COLUMN_OPTION, + TIME_FILTER_LABELS, + DEFAULT_METRICS, +} from '../constants'; export const dndGroupByControl: SharedControlConfig<'DndColumnSelect'> = { type: 'DndColumnSelect', @@ -36,17 +41,17 @@ export const dndGroupByControl: SharedControlConfig<'DndColumnSelect'> = { ), mapStateToProps(state, { includeTime }) { const newState: ExtraControlProps = {}; - const dataset = state.datasource as Dataset; - if (state.datasource) { - const options = dataset.columns.filter(c => c.groupby); + const { datasource } = state; + if (datasource?.columns?.hasOwnProperty('groupby')) { + const options = (datasource as Dataset).columns.filter(c => c.groupby); if (includeTime) { options.unshift(TIME_COLUMN_OPTION); } newState.options = Object.fromEntries( options.map(option => [option.column_name, option]), ); - newState.savedMetrics = dataset.metrics || []; - } + newState.savedMetrics = (datasource as Dataset).metrics || []; + } else newState.options = datasource?.columns; return newState; }, }; @@ -83,18 +88,18 @@ export const dnd_adhoc_filters: SharedControlConfig<'DndFilterSelect'> = { label: t('Filters'), default: [], description: '', - mapStateToProps: ({ datasource, form_data }) => { - const dataset = datasource as Dataset; - - return { - columns: dataset?.columns.filter(c => c.filterable) || [], - savedMetrics: dataset?.metrics || [], - // current active adhoc metrics - selectedMetrics: - form_data.metrics || (form_data.metric ? [form_data.metric] : []), - datasource, - }; - }, + mapStateToProps: ({ datasource, form_data }) => ({ + columns: datasource?.hasOwnProperty('filterable') + ? (datasource as Dataset)?.columns.filter(c => c.filterable) + : datasource?.columns || [], + savedMetrics: datasource?.hasOwnProperty('metrics') + ? (datasource as Dataset)?.metrics || [] + : DEFAULT_METRICS, + // current active adhoc metrics + selectedMetrics: + form_data.metrics || (form_data.metric ? [form_data.metric] : []), + datasource, + }), provideFormDataToProps: true, }; @@ -103,16 +108,14 @@ export const dnd_adhoc_metrics: SharedControlConfig<'DndMetricSelect'> = { multi: true, label: t('Metrics'), validators: [validateNonEmpty], - mapStateToProps: ({ datasource }) => { - const dataset = datasource as Dataset; - - return { - columns: dataset?.columns || [], - savedMetrics: dataset?.metrics || [], - datasource, - datasourceType: dataset?.type, - }; - }, + mapStateToProps: ({ datasource }) => ({ + columns: datasource?.columns || [], + savedMetrics: datasource?.hasOwnProperty('metrics') + ? (datasource as Dataset)?.metrics || [] + : DEFAULT_METRICS, + datasource, + datasourceType: datasource?.type, + }), description: t('One or many metrics to display'), }; @@ -137,16 +140,14 @@ export const dnd_sort_by: SharedControlConfig<'DndMetricSelect'> = { 'Metric used to define how the top series are sorted if a series or row limit is present. ' + 'If undefined reverts to the first metric (where appropriate).', ), - mapStateToProps: ({ datasource }) => { - const dataset = datasource as Dataset; - - return { - columns: dataset?.columns || [], - savedMetrics: dataset?.metrics || [], - datasource, - datasourceType: dataset?.type, - }; - }, + mapStateToProps: ({ datasource }) => ({ + columns: datasource?.columns || [], + savedMetrics: datasource?.hasOwnProperty('metrics') + ? (datasource as Dataset)?.metrics || [] + : DEFAULT_METRICS, + datasource, + datasourceType: datasource?.type, + }), }; export const dnd_size: SharedControlConfig<'DndMetricSelect'> = { @@ -191,15 +192,30 @@ export const dnd_granularity_sqla: typeof dndGroupByControl = { : 'Drop temporal column here', ), mapStateToProps: ({ datasource }) => { - const dataset = datasource as Dataset; - const temporalColumns = dataset?.columns.filter(c => c.is_dttm) ?? []; + if (datasource?.columns?.hasOwnProperty('column_name')) { + const temporalColumns = + (datasource as Dataset)?.columns.filter(c => c.is_dttm) ?? []; + const options = Object.fromEntries( + temporalColumns.map(option => [option.column_name, option]), + ); + return { + options, + default: + (datasource as Dataset)?.main_dttm_col || + temporalColumns[0]?.column_name || + null, + isTemporal: true, + }; + } + + const temporalColumns = + (datasource as QueryResponse)?.columns.filter(c => c.is_dttm) ?? []; const options = Object.fromEntries( - temporalColumns.map(option => [option.column_name, option]), + temporalColumns.map(option => [option.name, option]), ); return { options, - default: - dataset?.main_dttm_col || temporalColumns[0]?.column_name || null, + default: temporalColumns[0]?.name || null, isTemporal: true, }; }, diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx index 8027bf2040923..d759ee9bd1ee2 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx +++ b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx @@ -45,6 +45,8 @@ import { legacyValidateInteger, validateNonEmpty, ComparisionType, + QueryResponse, + QueryColumn, } from '@superset-ui/core'; import { @@ -56,7 +58,11 @@ import { DEFAULT_TIME_FORMAT, DEFAULT_NUMBER_FORMAT, } from '../utils'; -import { TIME_FILTER_LABELS, TIME_COLUMN_OPTION } from '../constants'; +import { + TIME_FILTER_LABELS, + TIME_COLUMN_OPTION, + DEFAULT_METRICS, +} from '../constants'; import { Metric, SharedControlConfig, @@ -132,14 +138,14 @@ const groupByControl: SharedControlConfig<'SelectControl', ColumnMeta> = { promptTextCreator: (label: unknown) => label, mapStateToProps(state, { includeTime }) { const newState: ExtraControlProps = {}; - const dataset = state.datasource as Dataset; - if (state.datasource) { - const options = dataset.columns.filter(c => c.groupby); + const { datasource } = state; + if (datasource?.columns?.hasOwnProperty('groupby')) { + const options = (datasource as Dataset).columns.filter(c => c.groupby); if (includeTime) { options.unshift(TIME_COLUMN_OPTION); } newState.options = options; - } + } else newState.options = datasource?.columns; return newState; }, commaChoosesOption: false, @@ -150,16 +156,14 @@ const metrics: SharedControlConfig<'MetricsControl'> = { multi: true, label: t('Metrics'), validators: [validateNonEmpty], - mapStateToProps: ({ datasource }) => { - const dataset = datasource as Dataset; - - return { - columns: dataset?.columns || [], - savedMetrics: dataset?.metrics || [], - datasource, - datasourceType: dataset?.type, - }; - }, + mapStateToProps: ({ datasource }) => ({ + columns: datasource?.columns || [], + savedMetrics: datasource?.hasOwnProperty('metrics') + ? (datasource as Dataset)?.metrics || [] + : DEFAULT_METRICS, + datasource, + datasourceType: datasource?.type, + }), description: t('One or many metrics to display'), }; @@ -298,16 +302,21 @@ const granularity_sqla: SharedControlConfig<'SelectControl', ColumnMeta> = { valueRenderer: c => , valueKey: 'column_name', mapStateToProps: state => { - const props: Partial> = {}; - const dataset = state.datasource as Dataset; - if (state.datasource) { - props.options = dataset.columns.filter(c => c.is_dttm); + const props: Partial> = {}; + const { datasource } = state; + const dataset = datasource as Dataset; + const query = datasource as QueryResponse; + if (datasource?.columns?.hasOwnProperty('main_dttm_col')) { + props.options = dataset.columns.filter((c: ColumnMeta) => c.is_dttm); props.default = null; if (dataset.main_dttm_col) { props.default = dataset.main_dttm_col; - } else if (props.options && props.options.length > 0) { - props.default = props.options[0].column_name; + } else if (props?.options) { + props.default = (props.options[0] as ColumnMeta).column_name; } + } else { + props.options = query?.columns.filter((c: QueryColumn) => c.is_dttm); + if (props?.options) props.default = props.options[0].name; } return props; }, @@ -406,15 +415,14 @@ const sort_by: SharedControlConfig<'MetricsControl'> = { 'Metric used to define how the top series are sorted if a series or row limit is present. ' + 'If undefined reverts to the first metric (where appropriate).', ), - mapStateToProps: ({ datasource }) => { - const dataset = datasource as Dataset; - return { - columns: dataset?.columns || [], - savedMetrics: dataset?.metrics || [], - datasource, - datasourceType: dataset?.type, - }; - }, + mapStateToProps: ({ datasource }) => ({ + columns: datasource?.columns || [], + savedMetrics: datasource?.hasOwnProperty('metrics') + ? (datasource as Dataset)?.metrics || [] + : DEFAULT_METRICS, + datasource, + datasourceType: datasource?.type, + }), }; const series: typeof groupByControl = { @@ -502,17 +510,18 @@ const adhoc_filters: SharedControlConfig<'AdhocFilterControl'> = { label: t('Filters'), default: [], description: '', - mapStateToProps: ({ datasource, form_data }) => { - const dataset = datasource as Dataset; - return { - columns: dataset?.columns.filter(c => c.filterable) || [], - savedMetrics: dataset?.metrics || [], - // current active adhoc metrics - selectedMetrics: - form_data.metrics || (form_data.metric ? [form_data.metric] : []), - datasource, - }; - }, + mapStateToProps: ({ datasource, form_data }) => ({ + columns: datasource?.hasOwnProperty('filterable') + ? (datasource as Dataset)?.columns.filter(c => c.filterable) + : datasource?.columns || [], + savedMetrics: datasource?.hasOwnProperty('metrics') + ? (datasource as Dataset)?.metrics || [] + : DEFAULT_METRICS, + // current active adhoc metrics + selectedMetrics: + form_data.metrics || (form_data.metric ? [form_data.metric] : []), + datasource, + }), }; const color_scheme: SharedControlConfig<'ColorSchemeControl'> = { diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/utils/columnChoices.ts b/superset-frontend/packages/superset-ui-chart-controls/src/utils/columnChoices.ts index 7977e9dc7ba86..23f17ba3ec789 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/utils/columnChoices.ts +++ b/superset-frontend/packages/superset-ui-chart-controls/src/utils/columnChoices.ts @@ -16,20 +16,30 @@ * specific language governing permissions and limitations * under the License. */ +import { QueryResponse } from '@superset-ui/core'; import { Dataset } from '../types'; /** * Convert Datasource columns to column choices */ export default function columnChoices( - datasource?: Dataset | null, + datasource?: Dataset | QueryResponse | null, ): [string, string][] { + if (datasource?.columns?.hasOwnProperty('column_name')) { + return ( + (datasource as Dataset)?.columns + .map((col): [string, string] => [ + col.column_name, + col.verbose_name || col.column_name, + ]) + .sort((opt1, opt2) => + opt1[1].toLowerCase() > opt2[1].toLowerCase() ? 1 : -1, + ) || [] + ); + } return ( - datasource?.columns - .map((col): [string, string] => [ - col.column_name, - col.verbose_name || col.column_name, - ]) + (datasource as QueryResponse)?.columns + .map((col): [string, string] => [col.name, col.name]) .sort((opt1, opt2) => opt1[1].toLowerCase() > opt2[1].toLowerCase() ? 1 : -1, ) || [] diff --git a/superset-frontend/plugins/legacy-plugin-chart-event-flow/src/controlPanel.tsx b/superset-frontend/plugins/legacy-plugin-chart-event-flow/src/controlPanel.tsx index f4871ff54849c..22d5c8ce4eeba 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-event-flow/src/controlPanel.tsx +++ b/superset-frontend/plugins/legacy-plugin-chart-event-flow/src/controlPanel.tsx @@ -27,7 +27,6 @@ import { formatSelectOptionsForRange, sections, SelectControlConfig, - Dataset, } from '@superset-ui/chart-controls'; const config: ControlPanelConfig = { @@ -45,7 +44,7 @@ const config: ControlPanelConfig = { label: t('Event Names'), description: t('Columns to display'), mapStateToProps: state => ({ - choices: columnChoices(state?.datasource as Dataset), + choices: columnChoices(state?.datasource), }), // choices is from `mapStateToProps` default: (control: ControlState) => diff --git a/superset-frontend/plugins/legacy-plugin-chart-heatmap/src/controlPanel.ts b/superset-frontend/plugins/legacy-plugin-chart-heatmap/src/controlPanel.ts index 558f746667da0..ab3eb2a59fa11 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-heatmap/src/controlPanel.ts +++ b/superset-frontend/plugins/legacy-plugin-chart-heatmap/src/controlPanel.ts @@ -30,7 +30,6 @@ import { formatSelectOptionsForRange, sections, dndEntity, - Dataset, } from '@superset-ui/chart-controls'; const sortAxisChoices = [ @@ -45,7 +44,7 @@ const allColumns = { default: null, description: t('Columns to display'), mapStateToProps: (state: ControlPanelState) => ({ - choices: columnChoices(state.datasource as Dataset), + choices: columnChoices(state.datasource), }), validators: [validateNonEmpty], }; diff --git a/superset-frontend/plugins/legacy-plugin-chart-histogram/src/controlPanel.ts b/superset-frontend/plugins/legacy-plugin-chart-histogram/src/controlPanel.ts index 3bc2e2ea7960d..26cec94749e6e 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-histogram/src/controlPanel.ts +++ b/superset-frontend/plugins/legacy-plugin-chart-histogram/src/controlPanel.ts @@ -29,7 +29,6 @@ import { formatSelectOptions, sections, dndColumnsControl, - Dataset, } from '@superset-ui/chart-controls'; const allColumns = { @@ -38,7 +37,7 @@ const allColumns = { default: null, description: t('Select the numeric columns to draw the histogram'), mapStateToProps: (state: ControlPanelState) => ({ - choices: columnChoices(state.datasource as Dataset), + choices: columnChoices(state.datasource), }), multi: true, validators: [validateNonEmpty], diff --git a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/controlPanel.ts b/superset-frontend/plugins/legacy-plugin-chart-map-box/src/controlPanel.ts index 8a9055b8dfbc1..8642e8946fe03 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-map-box/src/controlPanel.ts +++ b/superset-frontend/plugins/legacy-plugin-chart-map-box/src/controlPanel.ts @@ -24,14 +24,13 @@ import { formatSelectOptions, sections, dndEntity, - Dataset, } from '@superset-ui/chart-controls'; const allColumns = { type: 'SelectControl', default: null, mapStateToProps: (state: ControlPanelState) => ({ - choices: columnChoices(state.datasource as Dataset), + choices: columnChoices(state.datasource), }), }; @@ -123,9 +122,7 @@ const config: ControlPanelConfig = { 'on the largest cluster', ), mapStateToProps: state => { - const datasourceChoices = columnChoices( - state.datasource as Dataset, - ); + const datasourceChoices = columnChoices(state.datasource); const choices: [string, string][] = formatSelectOptions([ 'Auto', ]); @@ -170,7 +167,7 @@ const config: ControlPanelConfig = { 'Leave empty to get a count of points in each cluster.', ), mapStateToProps: state => ({ - choices: columnChoices(state.datasource as Dataset), + choices: columnChoices(state.datasource), }), }, }, diff --git a/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/metrics.tsx b/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/metrics.tsx index 1e9f431e21a21..0960d3309d7bf 100644 --- a/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/metrics.tsx +++ b/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/metrics.tsx @@ -23,6 +23,7 @@ import { sharedControls, Dataset, ColumnMeta, + DEFAULT_METRICS, } from '@superset-ui/chart-controls'; import { FeatureFlag, isFeatureEnabled, t } from '@superset-ui/core'; import { getQueryMode, isAggMode, validateAggControlValues } from './shared'; @@ -38,9 +39,11 @@ const percentMetrics: typeof sharedControls.metrics = { resetOnHide: false, mapStateToProps: ({ datasource, controls }, controlState) => ({ columns: datasource?.columns || [], - savedMetrics: (datasource as Dataset)?.metrics || [], + savedMetrics: datasource?.hasOwnProperty('metrics') + ? (datasource as Dataset)?.metrics || [] + : DEFAULT_METRICS, datasource, - datasourceType: (datasource as Dataset)?.type, + datasourceType: datasource?.type, queryMode: getQueryMode(controls), externalValidationErrors: validateAggControlValues(controls, [ controls.groupby?.value, @@ -81,7 +84,9 @@ export const metricsControlSetItem: ControlSetItem = { (c: ColumnMeta) => c.filterable, ) : datasource?.columns, - savedMetrics: (datasource as Dataset)?.metrics || [], + savedMetrics: datasource?.hasOwnProperty('metrics') + ? (datasource as Dataset)?.metrics || [] + : DEFAULT_METRICS, // current active adhoc metrics selectedMetrics: form_data.metrics || (form_data.metric ? [form_data.metric] : []), diff --git a/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/orderBy.tsx b/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/orderBy.tsx index 0466b5a155e93..93002bd49b361 100644 --- a/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/orderBy.tsx +++ b/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/orderBy.tsx @@ -29,7 +29,9 @@ export const orderByControlSetItem: ControlSetItem = { multi: true, default: [], mapStateToProps: ({ datasource }) => ({ - choices: (datasource as Dataset)?.order_by_choices || [], + choices: datasource?.hasOwnProperty('order_by_choices') + ? (datasource as Dataset)?.order_by_choices + : datasource?.columns || [], }), visibility: isRawMode, resetOnHide: false, diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/controlPanel.tsx index 9b89b661c64f2..bd0433749082a 100644 --- a/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-pivot-table/src/plugin/controlPanel.tsx @@ -350,8 +350,11 @@ const config: ControlPanelConfig = { const values = (explore?.controls?.metrics?.value as QueryFormMetric[]) ?? []; - const verboseMap = - (explore?.datasource as Dataset)?.verbose_map ?? {}; + const verboseMap = explore?.datasource?.hasOwnProperty( + 'verbose_map', + ) + ? (explore?.datasource as Dataset)?.verbose_map + : explore?.datasource?.columns ?? {}; const metricColumn = values.map(value => { if (typeof value === 'string') { return { value, label: verboseMap[value] ?? value }; diff --git a/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx index e977933c7990a..5ac75073ed88a 100644 --- a/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx @@ -46,6 +46,7 @@ import { emitFilterControl, Dataset, ColumnMeta, + DEFAULT_METRICS, } from '@superset-ui/chart-controls'; import i18n from './i18n'; @@ -129,14 +130,12 @@ const dnd_all_columns: typeof sharedControls.groupby = { default: [], mapStateToProps({ datasource, controls }, controlState) { const newState: ExtraControlProps = {}; - if (datasource) { - if (datasource?.columns?.hasOwnProperty('filterable')) { - const options = (datasource as Dataset).columns; - newState.options = Object.fromEntries( - options.map((option: ColumnMeta) => [option.column_name, option]), - ); - } else newState.options = datasource.columns; - } + if (datasource?.columns?.hasOwnProperty('column_name')) { + const options = (datasource as Dataset).columns; + newState.options = Object.fromEntries( + options.map((option: ColumnMeta) => [option.column_name, option]), + ); + } else newState.options = datasource?.columns; newState.queryMode = getQueryMode(controls); newState.externalValidationErrors = isRawMode({ controls }) && ensureIsArray(controlState.value).length === 0 @@ -159,9 +158,11 @@ const percent_metrics: typeof sharedControls.metrics = { resetOnHide: false, mapStateToProps: ({ datasource, controls }, controlState) => ({ columns: datasource?.columns || [], - savedMetrics: (datasource as Dataset)?.metrics || [], + savedMetrics: datasource?.hasOwnProperty('metrics') + ? (datasource as Dataset)?.metrics || [] + : DEFAULT_METRICS, datasource, - datasourceType: (datasource as Dataset)?.type, + datasourceType: datasource?.type, queryMode: getQueryMode(controls), externalValidationErrors: validateAggControlValues(controls, [ controls.groupby?.value, @@ -238,7 +239,9 @@ const config: ControlPanelConfig = { (c: ColumnMeta) => c.filterable, ) : datasource?.columns, - savedMetrics: (datasource as Dataset)?.metrics || [], + savedMetrics: datasource?.hasOwnProperty('metrics') + ? (datasource as Dataset)?.metrics || [] + : DEFAULT_METRICS, // current active adhoc metrics selectedMetrics: form_data.metrics || @@ -288,7 +291,9 @@ const config: ControlPanelConfig = { multi: true, default: [], mapStateToProps: ({ datasource }) => ({ - choices: (datasource as Dataset)?.order_by_choices || [], + choices: datasource?.hasOwnProperty('order_by_choices') + ? (datasource as Dataset)?.order_by_choices + : datasource?.columns || [], }), visibility: isRawMode, resetOnHide: false, @@ -513,8 +518,11 @@ const config: ControlPanelConfig = { return true; }, mapStateToProps(explore, _, chart) { - const verboseMap = - (explore?.datasource as Dataset)?.verbose_map ?? {}; + const verboseMap = explore?.datasource?.hasOwnProperty( + 'verbose_map', + ) + ? (explore?.datasource as Dataset)?.verbose_map + : explore?.datasource?.columns ?? {}; const { colnames, coltypes } = chart?.queriesResponse?.[0] ?? {}; const numericColumns = diff --git a/superset-frontend/src/explore/fixtures.tsx b/superset-frontend/src/explore/fixtures.tsx index 4cb9d9d42cb87..78579b82e49c3 100644 --- a/superset-frontend/src/explore/fixtures.tsx +++ b/superset-frontend/src/explore/fixtures.tsx @@ -24,7 +24,6 @@ import { ColumnOption, ControlConfig, ControlPanelSectionConfig, - Dataset, } from '@superset-ui/chart-controls'; export const controlPanelSectionsChartOptions: (ControlPanelSectionConfig | null)[] = @@ -100,7 +99,7 @@ export const controlPanelSectionsChartOptionsTable: ControlPanelSectionConfig[] optionRenderer: c => , valueKey: 'column_name', mapStateToProps: stateRef => ({ - options: (stateRef.datasource as Dataset)?.columns || [], + options: stateRef.datasource?.columns || [], }), freeForm: true, } as ControlConfig<'SelectControl', ColumnMeta>, From 1773954dfbfbcc827abc782354243598393f52bb Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Thu, 2 Jun 2022 15:17:28 -0500 Subject: [PATCH 42/57] Fix columnChoices --- .../superset-ui-chart-controls/src/utils/columnChoices.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/utils/columnChoices.ts b/superset-frontend/packages/superset-ui-chart-controls/src/utils/columnChoices.ts index 23f17ba3ec789..0387717ff7e3f 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/utils/columnChoices.ts +++ b/superset-frontend/packages/superset-ui-chart-controls/src/utils/columnChoices.ts @@ -25,7 +25,7 @@ import { Dataset } from '../types'; export default function columnChoices( datasource?: Dataset | QueryResponse | null, ): [string, string][] { - if (datasource?.columns?.hasOwnProperty('column_name')) { + if (datasource?.columns[0]?.hasOwnProperty('column_name')) { return ( (datasource as Dataset)?.columns .map((col): [string, string] => [ From 20ede6d62ac0779a376864c8836c579322062bfb Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Thu, 2 Jun 2022 15:37:50 -0500 Subject: [PATCH 43/57] Fix all incorrect column property checks --- .../src/shared-controls/dndControls.tsx | 6 +++--- .../src/shared-controls/index.tsx | 6 +++--- .../legacy-preset-chart-deckgl/src/utilities/controls.jsx | 2 +- .../plugin-chart-handlebars/src/plugin/controls/columns.tsx | 2 +- .../plugin-chart-handlebars/src/plugin/controls/metrics.tsx | 2 +- .../plugins/plugin-chart-table/src/controlPanel.tsx | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx index 597ac4b11a918..bb4cc6156841d 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx +++ b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx @@ -42,7 +42,7 @@ export const dndGroupByControl: SharedControlConfig<'DndColumnSelect'> = { mapStateToProps(state, { includeTime }) { const newState: ExtraControlProps = {}; const { datasource } = state; - if (datasource?.columns?.hasOwnProperty('groupby')) { + if (datasource?.columns[0]?.hasOwnProperty('groupby')) { const options = (datasource as Dataset).columns.filter(c => c.groupby); if (includeTime) { options.unshift(TIME_COLUMN_OPTION); @@ -89,7 +89,7 @@ export const dnd_adhoc_filters: SharedControlConfig<'DndFilterSelect'> = { default: [], description: '', mapStateToProps: ({ datasource, form_data }) => ({ - columns: datasource?.hasOwnProperty('filterable') + columns: datasource?.columns[0]?.hasOwnProperty('filterable') ? (datasource as Dataset)?.columns.filter(c => c.filterable) : datasource?.columns || [], savedMetrics: datasource?.hasOwnProperty('metrics') @@ -192,7 +192,7 @@ export const dnd_granularity_sqla: typeof dndGroupByControl = { : 'Drop temporal column here', ), mapStateToProps: ({ datasource }) => { - if (datasource?.columns?.hasOwnProperty('column_name')) { + if (datasource?.columns[0]?.hasOwnProperty('column_name')) { const temporalColumns = (datasource as Dataset)?.columns.filter(c => c.is_dttm) ?? []; const options = Object.fromEntries( diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx index d759ee9bd1ee2..8ec39f76d07c8 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx +++ b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx @@ -139,7 +139,7 @@ const groupByControl: SharedControlConfig<'SelectControl', ColumnMeta> = { mapStateToProps(state, { includeTime }) { const newState: ExtraControlProps = {}; const { datasource } = state; - if (datasource?.columns?.hasOwnProperty('groupby')) { + if (datasource?.columns[0]?.hasOwnProperty('groupby')) { const options = (datasource as Dataset).columns.filter(c => c.groupby); if (includeTime) { options.unshift(TIME_COLUMN_OPTION); @@ -306,7 +306,7 @@ const granularity_sqla: SharedControlConfig<'SelectControl', ColumnMeta> = { const { datasource } = state; const dataset = datasource as Dataset; const query = datasource as QueryResponse; - if (datasource?.columns?.hasOwnProperty('main_dttm_col')) { + if (datasource?.columns[0]?.hasOwnProperty('main_dttm_col')) { props.options = dataset.columns.filter((c: ColumnMeta) => c.is_dttm); props.default = null; if (dataset.main_dttm_col) { @@ -511,7 +511,7 @@ const adhoc_filters: SharedControlConfig<'AdhocFilterControl'> = { default: [], description: '', mapStateToProps: ({ datasource, form_data }) => ({ - columns: datasource?.hasOwnProperty('filterable') + columns: datasource?.columns[0]?.hasOwnProperty('filterable') ? (datasource as Dataset)?.columns.filter(c => c.filterable) : datasource?.columns || [], savedMetrics: datasource?.hasOwnProperty('metrics') diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/controls.jsx b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/controls.jsx index 03092e7316af6..9e6d2b0d84665 100644 --- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/controls.jsx +++ b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/utilities/controls.jsx @@ -17,7 +17,7 @@ * under the License. */ export function columnChoices(datasource) { - if (datasource && datasource.columns) { + if (datasource?.columns) { return datasource.columns .map(col => [col.column_name, col.verbose_name || col.column_name]) .sort((opt1, opt2) => diff --git a/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/columns.tsx b/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/columns.tsx index ca628a0d122f5..3aec61dc4060a 100644 --- a/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/columns.tsx +++ b/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/columns.tsx @@ -65,7 +65,7 @@ const dndAllColumns: typeof sharedControls.groupby = { mapStateToProps({ datasource, controls }, controlState) { const newState: ExtraControlProps = {}; if (datasource) { - if (datasource?.columns?.hasOwnProperty('filterable')) { + if (datasource?.columns[0]?.hasOwnProperty('filterable')) { const options = (datasource as Dataset).columns; newState.options = Object.fromEntries( options.map((option: ColumnMeta) => [option.column_name, option]), diff --git a/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/metrics.tsx b/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/metrics.tsx index 0960d3309d7bf..99c223ab0f3b2 100644 --- a/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/metrics.tsx +++ b/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/metrics.tsx @@ -79,7 +79,7 @@ export const metricsControlSetItem: ControlSetItem = { { controls, datasource, form_data }: ControlPanelState, controlState: ControlState, ) => ({ - columns: datasource?.columns?.hasOwnProperty('filterable') + columns: datasource?.columns[0]?.hasOwnProperty('filterable') ? (datasource as Dataset)?.columns?.filter( (c: ColumnMeta) => c.filterable, ) diff --git a/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx index 5ac75073ed88a..e322a091279cc 100644 --- a/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx @@ -130,7 +130,7 @@ const dnd_all_columns: typeof sharedControls.groupby = { default: [], mapStateToProps({ datasource, controls }, controlState) { const newState: ExtraControlProps = {}; - if (datasource?.columns?.hasOwnProperty('column_name')) { + if (datasource?.columns[0]?.hasOwnProperty('column_name')) { const options = (datasource as Dataset).columns; newState.options = Object.fromEntries( options.map((option: ColumnMeta) => [option.column_name, option]), @@ -234,7 +234,7 @@ const config: ControlPanelConfig = { { controls, datasource, form_data }: ControlPanelState, controlState: ControlState, ) => ({ - columns: datasource?.columns?.hasOwnProperty('filterable') + columns: datasource?.columns[0]?.hasOwnProperty('filterable') ? (datasource as Dataset)?.columns?.filter( (c: ColumnMeta) => c.filterable, ) From ccd3e3e800b99db12bb2117793a77a243c60240d Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Fri, 3 Jun 2022 13:19:10 -0500 Subject: [PATCH 44/57] Fix logic on dndGroupByControl --- .../superset-ui-chart-controls/src/constants.ts | 17 +++++++++++++++-- .../src/shared-controls/dndControls.tsx | 17 ++++++++++++++--- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/constants.ts b/superset-frontend/packages/superset-ui-chart-controls/src/constants.ts index 39988f87a4d5b..2dfa32b93b265 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/constants.ts +++ b/superset-frontend/packages/superset-ui-chart-controls/src/constants.ts @@ -16,7 +16,14 @@ * specific language governing permissions and limitations * under the License. */ -import { t, QueryMode, DTTM_ALIAS, GenericDataType } from '@superset-ui/core'; +import { + t, + QueryMode, + DTTM_ALIAS, + GenericDataType, + QueryColumn, + DatasourceType, +} from '@superset-ui/core'; import { ColumnMeta } from './types'; // eslint-disable-next-line import/prefer-default-export @@ -32,7 +39,7 @@ export const COLUMN_NAME_ALIASES: Record = { [DTTM_ALIAS]: t('Time'), }; -export const TIME_COLUMN_OPTION: ColumnMeta = { +export const DATASET_TIME_COLUMN_OPTION: ColumnMeta = { verbose_name: COLUMN_NAME_ALIASES[DTTM_ALIAS], column_name: DTTM_ALIAS, type_generic: GenericDataType.TEMPORAL, @@ -41,6 +48,12 @@ export const TIME_COLUMN_OPTION: ColumnMeta = { ), }; +export const QUERY_TIME_COLUMN_OPTION: QueryColumn = { + name: DTTM_ALIAS, + type: DatasourceType.Query, + is_dttm: false, +}; + export const QueryModeLabel = { [QueryMode.aggregate]: t('Aggregate'), [QueryMode.raw]: t('Raw records'), diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx index bb4cc6156841d..49ff668365227 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx +++ b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx @@ -20,16 +20,18 @@ import { FeatureFlag, isFeatureEnabled, + QueryColumn, QueryResponse, t, validateNonEmpty, } from '@superset-ui/core'; import { ExtraControlProps, SharedControlConfig, Dataset } from '../types'; import { - TIME_COLUMN_OPTION, + DATASET_TIME_COLUMN_OPTION, TIME_FILTER_LABELS, DEFAULT_METRICS, } from '../constants'; +import { QUERY_TIME_COLUMN_OPTION } from '..'; export const dndGroupByControl: SharedControlConfig<'DndColumnSelect'> = { type: 'DndColumnSelect', @@ -45,13 +47,22 @@ export const dndGroupByControl: SharedControlConfig<'DndColumnSelect'> = { if (datasource?.columns[0]?.hasOwnProperty('groupby')) { const options = (datasource as Dataset).columns.filter(c => c.groupby); if (includeTime) { - options.unshift(TIME_COLUMN_OPTION); + options.unshift(DATASET_TIME_COLUMN_OPTION); } newState.options = Object.fromEntries( options.map(option => [option.column_name, option]), ); newState.savedMetrics = (datasource as Dataset).metrics || []; - } else newState.options = datasource?.columns; + } else { + const options = datasource?.columns; + if (includeTime) { + (options as QueryColumn[])?.unshift(QUERY_TIME_COLUMN_OPTION); + } + newState.options = Object.fromEntries( + (options as QueryColumn[])?.map(option => [option.name, option]), + ); + newState.options = datasource?.columns; + } return newState; }, }; From a5855462a4ba47258f598734a94c9df385715ea1 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Fri, 3 Jun 2022 13:26:39 -0500 Subject: [PATCH 45/57] Dry up savedMetrics type check --- .../src/shared-controls/dndControls.tsx | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx index 49ff668365227..673b84c4ca74c 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx +++ b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx @@ -33,6 +33,11 @@ import { } from '../constants'; import { QUERY_TIME_COLUMN_OPTION } from '..'; +const savedMetricsTypeCheck = (datasource: Dataset | QueryResponse | null) => + datasource?.hasOwnProperty('metrics') + ? (datasource as Dataset)?.metrics || [] + : DEFAULT_METRICS; + export const dndGroupByControl: SharedControlConfig<'DndColumnSelect'> = { type: 'DndColumnSelect', label: t('Dimensions'), @@ -103,9 +108,7 @@ export const dnd_adhoc_filters: SharedControlConfig<'DndFilterSelect'> = { columns: datasource?.columns[0]?.hasOwnProperty('filterable') ? (datasource as Dataset)?.columns.filter(c => c.filterable) : datasource?.columns || [], - savedMetrics: datasource?.hasOwnProperty('metrics') - ? (datasource as Dataset)?.metrics || [] - : DEFAULT_METRICS, + savedMetrics: savedMetricsTypeCheck(datasource), // current active adhoc metrics selectedMetrics: form_data.metrics || (form_data.metric ? [form_data.metric] : []), @@ -121,9 +124,7 @@ export const dnd_adhoc_metrics: SharedControlConfig<'DndMetricSelect'> = { validators: [validateNonEmpty], mapStateToProps: ({ datasource }) => ({ columns: datasource?.columns || [], - savedMetrics: datasource?.hasOwnProperty('metrics') - ? (datasource as Dataset)?.metrics || [] - : DEFAULT_METRICS, + savedMetrics: savedMetricsTypeCheck(datasource), datasource, datasourceType: datasource?.type, }), @@ -153,9 +154,7 @@ export const dnd_sort_by: SharedControlConfig<'DndMetricSelect'> = { ), mapStateToProps: ({ datasource }) => ({ columns: datasource?.columns || [], - savedMetrics: datasource?.hasOwnProperty('metrics') - ? (datasource as Dataset)?.metrics || [] - : DEFAULT_METRICS, + savedMetrics: savedMetricsTypeCheck(datasource), datasource, datasourceType: datasource?.type, }), From 5c93394b6d5c1bf6c89cb682bab0617cfe4ca1e9 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Fri, 3 Jun 2022 14:56:25 -0500 Subject: [PATCH 46/57] Fixed TIME_COLUMN_OPTION --- .../superset-ui-chart-controls/src/shared-controls/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx index 8ec39f76d07c8..fc0669647f89b 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx +++ b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx @@ -60,7 +60,7 @@ import { } from '../utils'; import { TIME_FILTER_LABELS, - TIME_COLUMN_OPTION, + DATASET_TIME_COLUMN_OPTION, DEFAULT_METRICS, } from '../constants'; import { @@ -142,7 +142,7 @@ const groupByControl: SharedControlConfig<'SelectControl', ColumnMeta> = { if (datasource?.columns[0]?.hasOwnProperty('groupby')) { const options = (datasource as Dataset).columns.filter(c => c.groupby); if (includeTime) { - options.unshift(TIME_COLUMN_OPTION); + options.unshift(DATASET_TIME_COLUMN_OPTION); } newState.options = options; } else newState.options = datasource?.columns; From b9db9d2c1b3bbafb16e1c38ae2647a7293cd5529 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Fri, 3 Jun 2022 15:33:16 -0500 Subject: [PATCH 47/57] Dried savedMetrics type check even further --- .../src/shared-controls/dndControls.tsx | 13 ++------ .../src/shared-controls/index.tsx | 19 ++++-------- .../src/utils/index.ts | 1 + .../src/utils/savedMetricsTypeCheck.ts | 30 +++++++++++++++++++ .../src/plugin/controls/metrics.tsx | 10 ++----- .../plugin-chart-table/src/controlPanel.tsx | 10 ++----- 6 files changed, 44 insertions(+), 39 deletions(-) create mode 100644 superset-frontend/packages/superset-ui-chart-controls/src/utils/savedMetricsTypeCheck.ts diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx index 673b84c4ca74c..a2a663dcffaaa 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx +++ b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx @@ -26,17 +26,8 @@ import { validateNonEmpty, } from '@superset-ui/core'; import { ExtraControlProps, SharedControlConfig, Dataset } from '../types'; -import { - DATASET_TIME_COLUMN_OPTION, - TIME_FILTER_LABELS, - DEFAULT_METRICS, -} from '../constants'; -import { QUERY_TIME_COLUMN_OPTION } from '..'; - -const savedMetricsTypeCheck = (datasource: Dataset | QueryResponse | null) => - datasource?.hasOwnProperty('metrics') - ? (datasource as Dataset)?.metrics || [] - : DEFAULT_METRICS; +import { DATASET_TIME_COLUMN_OPTION, TIME_FILTER_LABELS } from '../constants'; +import { QUERY_TIME_COLUMN_OPTION, savedMetricsTypeCheck } from '..'; export const dndGroupByControl: SharedControlConfig<'DndColumnSelect'> = { type: 'DndColumnSelect', diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx index fc0669647f89b..3145385e504ac 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx +++ b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx @@ -57,12 +57,9 @@ import { D3_TIME_FORMAT_DOCS, DEFAULT_TIME_FORMAT, DEFAULT_NUMBER_FORMAT, + savedMetricsTypeCheck, } from '../utils'; -import { - TIME_FILTER_LABELS, - DATASET_TIME_COLUMN_OPTION, - DEFAULT_METRICS, -} from '../constants'; +import { TIME_FILTER_LABELS, DATASET_TIME_COLUMN_OPTION } from '../constants'; import { Metric, SharedControlConfig, @@ -158,9 +155,7 @@ const metrics: SharedControlConfig<'MetricsControl'> = { validators: [validateNonEmpty], mapStateToProps: ({ datasource }) => ({ columns: datasource?.columns || [], - savedMetrics: datasource?.hasOwnProperty('metrics') - ? (datasource as Dataset)?.metrics || [] - : DEFAULT_METRICS, + savedMetrics: savedMetricsTypeCheck(datasource), datasource, datasourceType: datasource?.type, }), @@ -417,9 +412,7 @@ const sort_by: SharedControlConfig<'MetricsControl'> = { ), mapStateToProps: ({ datasource }) => ({ columns: datasource?.columns || [], - savedMetrics: datasource?.hasOwnProperty('metrics') - ? (datasource as Dataset)?.metrics || [] - : DEFAULT_METRICS, + savedMetrics: savedMetricsTypeCheck(datasource), datasource, datasourceType: datasource?.type, }), @@ -514,9 +507,7 @@ const adhoc_filters: SharedControlConfig<'AdhocFilterControl'> = { columns: datasource?.columns[0]?.hasOwnProperty('filterable') ? (datasource as Dataset)?.columns.filter(c => c.filterable) : datasource?.columns || [], - savedMetrics: datasource?.hasOwnProperty('metrics') - ? (datasource as Dataset)?.metrics || [] - : DEFAULT_METRICS, + savedMetrics: savedMetricsTypeCheck(datasource), // current active adhoc metrics selectedMetrics: form_data.metrics || (form_data.metric ? [form_data.metric] : []), diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/utils/index.ts b/superset-frontend/packages/superset-ui-chart-controls/src/utils/index.ts index bfb5b5e824e64..38efef788f28d 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/utils/index.ts +++ b/superset-frontend/packages/superset-ui-chart-controls/src/utils/index.ts @@ -22,3 +22,4 @@ export * from './expandControlConfig'; export * from './getColorFormatters'; export { default as mainMetric } from './mainMetric'; export { default as columnChoices } from './columnChoices'; +export * from './savedMetricsTypeCheck'; diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/utils/savedMetricsTypeCheck.ts b/superset-frontend/packages/superset-ui-chart-controls/src/utils/savedMetricsTypeCheck.ts new file mode 100644 index 0000000000000..c6164654bfaa9 --- /dev/null +++ b/superset-frontend/packages/superset-ui-chart-controls/src/utils/savedMetricsTypeCheck.ts @@ -0,0 +1,30 @@ +/* eslint-disable camelcase */ +/** + * 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 { QueryResponse } from '@superset-ui/core'; +import { Dataset } from '../types'; +import { DEFAULT_METRICS } from '..'; + +export const savedMetricsTypeCheck = ( + datasource: Dataset | QueryResponse | null, +) => + datasource?.hasOwnProperty('metrics') + ? (datasource as Dataset)?.metrics || [] + : DEFAULT_METRICS; diff --git a/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/metrics.tsx b/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/metrics.tsx index 99c223ab0f3b2..d7f664c964451 100644 --- a/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/metrics.tsx +++ b/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/metrics.tsx @@ -23,7 +23,7 @@ import { sharedControls, Dataset, ColumnMeta, - DEFAULT_METRICS, + savedMetricsTypeCheck, } from '@superset-ui/chart-controls'; import { FeatureFlag, isFeatureEnabled, t } from '@superset-ui/core'; import { getQueryMode, isAggMode, validateAggControlValues } from './shared'; @@ -39,9 +39,7 @@ const percentMetrics: typeof sharedControls.metrics = { resetOnHide: false, mapStateToProps: ({ datasource, controls }, controlState) => ({ columns: datasource?.columns || [], - savedMetrics: datasource?.hasOwnProperty('metrics') - ? (datasource as Dataset)?.metrics || [] - : DEFAULT_METRICS, + savedMetrics: savedMetricsTypeCheck(datasource), datasource, datasourceType: datasource?.type, queryMode: getQueryMode(controls), @@ -84,9 +82,7 @@ export const metricsControlSetItem: ControlSetItem = { (c: ColumnMeta) => c.filterable, ) : datasource?.columns, - savedMetrics: datasource?.hasOwnProperty('metrics') - ? (datasource as Dataset)?.metrics || [] - : DEFAULT_METRICS, + savedMetrics: savedMetricsTypeCheck(datasource), // current active adhoc metrics selectedMetrics: form_data.metrics || (form_data.metric ? [form_data.metric] : []), diff --git a/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx index e322a091279cc..3306c92f27a6e 100644 --- a/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx @@ -46,7 +46,7 @@ import { emitFilterControl, Dataset, ColumnMeta, - DEFAULT_METRICS, + savedMetricsTypeCheck, } from '@superset-ui/chart-controls'; import i18n from './i18n'; @@ -158,9 +158,7 @@ const percent_metrics: typeof sharedControls.metrics = { resetOnHide: false, mapStateToProps: ({ datasource, controls }, controlState) => ({ columns: datasource?.columns || [], - savedMetrics: datasource?.hasOwnProperty('metrics') - ? (datasource as Dataset)?.metrics || [] - : DEFAULT_METRICS, + savedMetrics: savedMetricsTypeCheck(datasource), datasource, datasourceType: datasource?.type, queryMode: getQueryMode(controls), @@ -239,9 +237,7 @@ const config: ControlPanelConfig = { (c: ColumnMeta) => c.filterable, ) : datasource?.columns, - savedMetrics: datasource?.hasOwnProperty('metrics') - ? (datasource as Dataset)?.metrics || [] - : DEFAULT_METRICS, + savedMetrics: savedMetricsTypeCheck(datasource), // current active adhoc metrics selectedMetrics: form_data.metrics || From cea65ce3bf31f7565bf0ddb75cebffbcb89ac1e2 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Fri, 3 Jun 2022 22:06:20 -0500 Subject: [PATCH 48/57] Change savedMetricsTypeCheck to defineSavedMetrics --- .../src/shared-controls/dndControls.tsx | 8 ++++---- .../src/shared-controls/index.tsx | 8 ++++---- .../{savedMetricsTypeCheck.ts => defineSavedMetrics.ts} | 2 +- .../superset-ui-chart-controls/src/utils/index.ts | 2 +- .../src/plugin/controls/metrics.tsx | 6 +++--- .../plugins/plugin-chart-table/src/controlPanel.tsx | 6 +++--- 6 files changed, 16 insertions(+), 16 deletions(-) rename superset-frontend/packages/superset-ui-chart-controls/src/utils/{savedMetricsTypeCheck.ts => defineSavedMetrics.ts} (96%) diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx index a2a663dcffaaa..c14d1ea6e5984 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx +++ b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx @@ -27,7 +27,7 @@ import { } from '@superset-ui/core'; import { ExtraControlProps, SharedControlConfig, Dataset } from '../types'; import { DATASET_TIME_COLUMN_OPTION, TIME_FILTER_LABELS } from '../constants'; -import { QUERY_TIME_COLUMN_OPTION, savedMetricsTypeCheck } from '..'; +import { QUERY_TIME_COLUMN_OPTION, defineSavedMetrics } from '..'; export const dndGroupByControl: SharedControlConfig<'DndColumnSelect'> = { type: 'DndColumnSelect', @@ -99,7 +99,7 @@ export const dnd_adhoc_filters: SharedControlConfig<'DndFilterSelect'> = { columns: datasource?.columns[0]?.hasOwnProperty('filterable') ? (datasource as Dataset)?.columns.filter(c => c.filterable) : datasource?.columns || [], - savedMetrics: savedMetricsTypeCheck(datasource), + savedMetrics: defineSavedMetrics(datasource), // current active adhoc metrics selectedMetrics: form_data.metrics || (form_data.metric ? [form_data.metric] : []), @@ -115,7 +115,7 @@ export const dnd_adhoc_metrics: SharedControlConfig<'DndMetricSelect'> = { validators: [validateNonEmpty], mapStateToProps: ({ datasource }) => ({ columns: datasource?.columns || [], - savedMetrics: savedMetricsTypeCheck(datasource), + savedMetrics: defineSavedMetrics(datasource), datasource, datasourceType: datasource?.type, }), @@ -145,7 +145,7 @@ export const dnd_sort_by: SharedControlConfig<'DndMetricSelect'> = { ), mapStateToProps: ({ datasource }) => ({ columns: datasource?.columns || [], - savedMetrics: savedMetricsTypeCheck(datasource), + savedMetrics: defineSavedMetrics(datasource), datasource, datasourceType: datasource?.type, }), diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx index 3145385e504ac..5ecca63d11a17 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx +++ b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx @@ -57,7 +57,7 @@ import { D3_TIME_FORMAT_DOCS, DEFAULT_TIME_FORMAT, DEFAULT_NUMBER_FORMAT, - savedMetricsTypeCheck, + defineSavedMetrics, } from '../utils'; import { TIME_FILTER_LABELS, DATASET_TIME_COLUMN_OPTION } from '../constants'; import { @@ -155,7 +155,7 @@ const metrics: SharedControlConfig<'MetricsControl'> = { validators: [validateNonEmpty], mapStateToProps: ({ datasource }) => ({ columns: datasource?.columns || [], - savedMetrics: savedMetricsTypeCheck(datasource), + savedMetrics: defineSavedMetrics(datasource), datasource, datasourceType: datasource?.type, }), @@ -412,7 +412,7 @@ const sort_by: SharedControlConfig<'MetricsControl'> = { ), mapStateToProps: ({ datasource }) => ({ columns: datasource?.columns || [], - savedMetrics: savedMetricsTypeCheck(datasource), + savedMetrics: defineSavedMetrics(datasource), datasource, datasourceType: datasource?.type, }), @@ -507,7 +507,7 @@ const adhoc_filters: SharedControlConfig<'AdhocFilterControl'> = { columns: datasource?.columns[0]?.hasOwnProperty('filterable') ? (datasource as Dataset)?.columns.filter(c => c.filterable) : datasource?.columns || [], - savedMetrics: savedMetricsTypeCheck(datasource), + savedMetrics: defineSavedMetrics(datasource), // current active adhoc metrics selectedMetrics: form_data.metrics || (form_data.metric ? [form_data.metric] : []), diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/utils/savedMetricsTypeCheck.ts b/superset-frontend/packages/superset-ui-chart-controls/src/utils/defineSavedMetrics.ts similarity index 96% rename from superset-frontend/packages/superset-ui-chart-controls/src/utils/savedMetricsTypeCheck.ts rename to superset-frontend/packages/superset-ui-chart-controls/src/utils/defineSavedMetrics.ts index c6164654bfaa9..4f0732164cd84 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/utils/savedMetricsTypeCheck.ts +++ b/superset-frontend/packages/superset-ui-chart-controls/src/utils/defineSavedMetrics.ts @@ -22,7 +22,7 @@ import { QueryResponse } from '@superset-ui/core'; import { Dataset } from '../types'; import { DEFAULT_METRICS } from '..'; -export const savedMetricsTypeCheck = ( +export const defineSavedMetrics = ( datasource: Dataset | QueryResponse | null, ) => datasource?.hasOwnProperty('metrics') diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/utils/index.ts b/superset-frontend/packages/superset-ui-chart-controls/src/utils/index.ts index 38efef788f28d..11c03e4ca1fac 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/utils/index.ts +++ b/superset-frontend/packages/superset-ui-chart-controls/src/utils/index.ts @@ -22,4 +22,4 @@ export * from './expandControlConfig'; export * from './getColorFormatters'; export { default as mainMetric } from './mainMetric'; export { default as columnChoices } from './columnChoices'; -export * from './savedMetricsTypeCheck'; +export * from './defineSavedMetrics'; diff --git a/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/metrics.tsx b/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/metrics.tsx index d7f664c964451..96eab55f92974 100644 --- a/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/metrics.tsx +++ b/superset-frontend/plugins/plugin-chart-handlebars/src/plugin/controls/metrics.tsx @@ -23,7 +23,7 @@ import { sharedControls, Dataset, ColumnMeta, - savedMetricsTypeCheck, + defineSavedMetrics, } from '@superset-ui/chart-controls'; import { FeatureFlag, isFeatureEnabled, t } from '@superset-ui/core'; import { getQueryMode, isAggMode, validateAggControlValues } from './shared'; @@ -39,7 +39,7 @@ const percentMetrics: typeof sharedControls.metrics = { resetOnHide: false, mapStateToProps: ({ datasource, controls }, controlState) => ({ columns: datasource?.columns || [], - savedMetrics: savedMetricsTypeCheck(datasource), + savedMetrics: defineSavedMetrics(datasource), datasource, datasourceType: datasource?.type, queryMode: getQueryMode(controls), @@ -82,7 +82,7 @@ export const metricsControlSetItem: ControlSetItem = { (c: ColumnMeta) => c.filterable, ) : datasource?.columns, - savedMetrics: savedMetricsTypeCheck(datasource), + savedMetrics: defineSavedMetrics(datasource), // current active adhoc metrics selectedMetrics: form_data.metrics || (form_data.metric ? [form_data.metric] : []), diff --git a/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx b/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx index 3306c92f27a6e..e06d583990436 100644 --- a/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx +++ b/superset-frontend/plugins/plugin-chart-table/src/controlPanel.tsx @@ -46,7 +46,7 @@ import { emitFilterControl, Dataset, ColumnMeta, - savedMetricsTypeCheck, + defineSavedMetrics, } from '@superset-ui/chart-controls'; import i18n from './i18n'; @@ -158,7 +158,7 @@ const percent_metrics: typeof sharedControls.metrics = { resetOnHide: false, mapStateToProps: ({ datasource, controls }, controlState) => ({ columns: datasource?.columns || [], - savedMetrics: savedMetricsTypeCheck(datasource), + savedMetrics: defineSavedMetrics(datasource), datasource, datasourceType: datasource?.type, queryMode: getQueryMode(controls), @@ -237,7 +237,7 @@ const config: ControlPanelConfig = { (c: ColumnMeta) => c.filterable, ) : datasource?.columns, - savedMetrics: savedMetricsTypeCheck(datasource), + savedMetrics: defineSavedMetrics(datasource), // current active adhoc metrics selectedMetrics: form_data.metrics || From 90c20b23a223352b9f61e472aa36f7ebf6a60c43 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Fri, 3 Jun 2022 22:07:53 -0500 Subject: [PATCH 49/57] Change datasourceTypeCheck to isValidDatasourceType --- .../src/explore/components/DatasourcePanel/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx index e786f4b30f30b..e8ea306814107 100644 --- a/superset-frontend/src/explore/components/DatasourcePanel/index.tsx +++ b/superset-frontend/src/explore/components/DatasourcePanel/index.tsx @@ -307,7 +307,7 @@ export default function DataSourcePanel({ return true; }; - const datasourceTypeCheck = + const isValidDatasourceType = datasource.type === DatasourceType.Dataset || datasource.type === DatasourceType.SlTable || datasource.type === DatasourceType.SavedQuery || @@ -327,7 +327,7 @@ export default function DataSourcePanel({ placeholder={t('Search Metrics & Columns')} />
- {datasourceTypeCheck && showInfoboxCheck() && ( + {isValidDatasourceType && showInfoboxCheck() && ( Date: Fri, 3 Jun 2022 22:11:51 -0500 Subject: [PATCH 50/57] Fix Query path in groupByControl --- .../src/shared-controls/index.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx index 5ecca63d11a17..ea43c098e2b99 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx +++ b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx @@ -86,6 +86,7 @@ import { dndSeries, dnd_adhoc_metric_2, } from './dndControls'; +import { QUERY_TIME_COLUMN_OPTION } from '..'; const categoricalSchemeRegistry = getCategoricalSchemeRegistry(); const sequentialSchemeRegistry = getSequentialSchemeRegistry(); @@ -138,11 +139,13 @@ const groupByControl: SharedControlConfig<'SelectControl', ColumnMeta> = { const { datasource } = state; if (datasource?.columns[0]?.hasOwnProperty('groupby')) { const options = (datasource as Dataset).columns.filter(c => c.groupby); - if (includeTime) { - options.unshift(DATASET_TIME_COLUMN_OPTION); - } + if (includeTime) options.unshift(DATASET_TIME_COLUMN_OPTION); + newState.options = options; + } else { + const options = (datasource as QueryResponse).columns; + if (includeTime) options.unshift(QUERY_TIME_COLUMN_OPTION); newState.options = options; - } else newState.options = datasource?.columns; + } return newState; }, commaChoosesOption: false, From ac671a4e7f65ce966a1d2c8d9f637d5bfb8cf64a Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Sat, 4 Jun 2022 10:02:17 -0500 Subject: [PATCH 51/57] dnd_granularity_sqla now sorts Query types with is_dttm at the top --- .../src/shared-controls/dndControls.tsx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx index c14d1ea6e5984..354cf959d7e03 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx +++ b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx @@ -195,7 +195,7 @@ export const dnd_granularity_sqla: typeof dndGroupByControl = { mapStateToProps: ({ datasource }) => { if (datasource?.columns[0]?.hasOwnProperty('column_name')) { const temporalColumns = - (datasource as Dataset)?.columns.filter(c => c.is_dttm) ?? []; + (datasource as Dataset)?.columns?.filter(c => c.is_dttm) ?? []; const options = Object.fromEntries( temporalColumns.map(option => [option.column_name, option]), ); @@ -209,14 +209,19 @@ export const dnd_granularity_sqla: typeof dndGroupByControl = { }; } - const temporalColumns = - (datasource as QueryResponse)?.columns.filter(c => c.is_dttm) ?? []; + const sortedQueryColumns = (datasource as QueryResponse)?.columns?.sort( + query => { + if (query?.is_dttm) return -1; + if (!query?.is_dttm) return 1; + return 0; + }, + ); const options = Object.fromEntries( - temporalColumns.map(option => [option.name, option]), + sortedQueryColumns.map(option => [option.name, option]), ); return { options, - default: temporalColumns[0]?.name || null, + default: sortedQueryColumns[0]?.name || null, isTemporal: true, }; }, From cf12f1daa99b62a4a7ca0dfc4ebb5a95c39b501f Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Mon, 6 Jun 2022 11:44:46 -0500 Subject: [PATCH 52/57] Fixed/cleaned query sort --- .../src/shared-controls/dndControls.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx index 354cf959d7e03..ce63590f740bb 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx +++ b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/dndControls.tsx @@ -210,11 +210,7 @@ export const dnd_granularity_sqla: typeof dndGroupByControl = { } const sortedQueryColumns = (datasource as QueryResponse)?.columns?.sort( - query => { - if (query?.is_dttm) return -1; - if (!query?.is_dttm) return 1; - return 0; - }, + query => (query?.is_dttm ? -1 : 1), ); const options = Object.fromEntries( sortedQueryColumns.map(option => [option.name, option]), From 379d9a9dd0850fad33bcb176a8c588b491358acd Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Mon, 6 Jun 2022 16:54:51 -0500 Subject: [PATCH 53/57] Add sortedQueryColumns and proper optional chaining to granularity_sqla --- .../src/shared-controls/index.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx index ea43c098e2b99..5ff32d50b0785 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx +++ b/superset-frontend/packages/superset-ui-chart-controls/src/shared-controls/index.tsx @@ -302,9 +302,8 @@ const granularity_sqla: SharedControlConfig<'SelectControl', ColumnMeta> = { mapStateToProps: state => { const props: Partial> = {}; const { datasource } = state; - const dataset = datasource as Dataset; - const query = datasource as QueryResponse; if (datasource?.columns[0]?.hasOwnProperty('main_dttm_col')) { + const dataset = datasource as Dataset; props.options = dataset.columns.filter((c: ColumnMeta) => c.is_dttm); props.default = null; if (dataset.main_dttm_col) { @@ -313,8 +312,11 @@ const granularity_sqla: SharedControlConfig<'SelectControl', ColumnMeta> = { props.default = (props.options[0] as ColumnMeta).column_name; } } else { - props.options = query?.columns.filter((c: QueryColumn) => c.is_dttm); - if (props?.options) props.default = props.options[0].name; + const sortedQueryColumns = (datasource as QueryResponse)?.columns?.sort( + query => (query?.is_dttm ? -1 : 1), + ); + props.options = sortedQueryColumns; + if (props?.options) props.default = props.options[0]?.name; } return props; }, From 23923f490d7d9c35082cb32674bfc89ba449d1a1 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Tue, 7 Jun 2022 00:02:15 -0500 Subject: [PATCH 54/57] Move testQuery to core-ui, add test coverage for Queries in columnChoices --- .../test/utils/columnChoices.test.tsx | 13 ++++- .../superset-ui-core/src/query/types/Query.ts | 54 +++++++++++++++++++ .../SaveDatasetModal.test.tsx | 3 +- superset-frontend/src/SqlLab/fixtures.ts | 39 -------------- 4 files changed, 66 insertions(+), 43 deletions(-) diff --git a/superset-frontend/packages/superset-ui-chart-controls/test/utils/columnChoices.test.tsx b/superset-frontend/packages/superset-ui-chart-controls/test/utils/columnChoices.test.tsx index d4e34c79c7fea..3224bbcc26d5c 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/test/utils/columnChoices.test.tsx +++ b/superset-frontend/packages/superset-ui-chart-controls/test/utils/columnChoices.test.tsx @@ -16,11 +16,11 @@ * specific language governing permissions and limitations * under the License. */ -import { DatasourceType } from '@superset-ui/core'; +import { DatasourceType, QueryResponse, testQuery } from '@superset-ui/core'; import { columnChoices } from '../../src'; describe('columnChoices()', () => { - it('should convert columns to choices', () => { + it('should convert columns to choices when source is a Dataset', () => { expect( columnChoices({ id: 1, @@ -56,4 +56,13 @@ describe('columnChoices()', () => { it('should return empty array when no columns', () => { expect(columnChoices(undefined)).toEqual([]); }); + + it('should convert columns to choices when source is a Query', () => { + expect(columnChoices(testQuery as QueryResponse)).toEqual([ + ['Column 1', 'Column 1'], + ['Column 2', 'Column 2'], + ['Column 3', 'Column 3'], + ]); + expect.anything(); + }); }); diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts b/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts index e863e0afa68e6..d4b672a7a3ad9 100644 --- a/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts +++ b/superset-frontend/packages/superset-ui-core/src/query/types/Query.ts @@ -321,4 +321,58 @@ export type QueryResults = { export type QueryResponse = Query & QueryResults; +export const testQuery: Query = { + id: 'clientId2353', + dbId: 1, + sql: 'SELECT * FROM something', + sqlEditorId: 'dfsadfs', + tab: 'unimportant', + tempTable: '', + ctas: false, + cached: false, + errorMessage: null, + extra: { progress: null }, + isDataPreview: false, + progress: 0, + resultsKey: null, + state: 'success', + tempSchema: null, + trackingUrl: null, + templateParams: null, + rows: 42, + queryLimit: 100, + limitingFactor: '', + endDttm: 1476910579693, + duration: '', + startDttm: 1476910566092.96, + time: {}, + user: {}, + userId: 1, + db: {}, + started: '', + querylink: {}, + queryId: 1, + executedSql: '', + output: '', + actions: {}, + type: DatasourceType.Query, + columns: [ + { + name: 'Column 1', + type: DatasourceType.Query, + is_dttm: false, + }, + { + name: 'Column 2', + type: DatasourceType.Query, + is_dttm: true, + }, + { + name: 'Column 3', + type: DatasourceType.Query, + is_dttm: false, + }, + ], +}; + export default {}; diff --git a/superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx b/superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx index c895867ac7d32..c35b5eb2b6022 100644 --- a/superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx +++ b/superset-frontend/src/SqlLab/components/SaveDatasetModal/SaveDatasetModal.test.tsx @@ -17,10 +17,9 @@ * under the License. */ import React from 'react'; -import { QueryResponse } from '@superset-ui/core'; +import { QueryResponse, testQuery } from '@superset-ui/core'; import { SaveDatasetModal } from 'src/SqlLab/components/SaveDatasetModal'; import { render, screen } from 'spec/helpers/testing-library'; -import { testQuery } from 'src/SqlLab/fixtures'; const mockedProps = { visible: true, diff --git a/superset-frontend/src/SqlLab/fixtures.ts b/superset-frontend/src/SqlLab/fixtures.ts index 97cefe0329ffb..5b12ee29213ee 100644 --- a/superset-frontend/src/SqlLab/fixtures.ts +++ b/superset-frontend/src/SqlLab/fixtures.ts @@ -19,7 +19,6 @@ import sinon from 'sinon'; import * as actions from 'src/SqlLab/actions/sqlLab'; import { ColumnKeyTypeType } from 'src/SqlLab/components/ColumnElement'; -import { DatasourceType, Query } from '@superset-ui/core'; export const mockedActions = sinon.stub({ ...actions }); @@ -573,41 +572,3 @@ export const query = { ctas: false, cached: false, }; - -export const testQuery: Query = { - id: 'clientId2353', - dbId: 1, - sql: 'SELECT * FROM something', - sqlEditorId: defaultQueryEditor.id, - tab: 'unimportant', - tempTable: '', - ctas: false, - cached: false, - errorMessage: null, - extra: { progress: null }, - isDataPreview: false, - progress: 0, - resultsKey: null, - state: 'success', - tempSchema: null, - trackingUrl: null, - templateParams: null, - rows: 42, - queryLimit: 100, - limitingFactor: '', - endDttm: 1476910579693, - duration: '', - startDttm: 1476910566092.96, - time: {}, - user: {}, - userId: 1, - db: {}, - started: '', - querylink: {}, - queryId: 1, - executedSql: '', - output: '', - actions: {}, - type: DatasourceType.Query, - columns: [], -}; From 430e43b2f628ea1bd043961b3ec321bed32564e4 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Tue, 7 Jun 2022 12:17:05 -0500 Subject: [PATCH 55/57] Moved DEFAULT_METRICS to core-ui and wrote a test for defineSavedMetrics --- .../src/constants.ts | 7 --- .../src/utils/defineSavedMetrics.ts | 3 +- .../test/utils/defineSavedMetrics.test.tsx | 54 +++++++++++++++++++ .../src/query/types/Datasource.ts | 7 +++ 4 files changed, 62 insertions(+), 9 deletions(-) create mode 100644 superset-frontend/packages/superset-ui-chart-controls/test/utils/defineSavedMetrics.test.tsx diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/constants.ts b/superset-frontend/packages/superset-ui-chart-controls/src/constants.ts index 2dfa32b93b265..265874f5e6661 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/constants.ts +++ b/superset-frontend/packages/superset-ui-chart-controls/src/constants.ts @@ -58,10 +58,3 @@ export const QueryModeLabel = { [QueryMode.aggregate]: t('Aggregate'), [QueryMode.raw]: t('Raw records'), }; - -export const DEFAULT_METRICS = [ - { - metric_name: 'COUNT(*)', - expression: 'COUNT(*)', - }, -]; diff --git a/superset-frontend/packages/superset-ui-chart-controls/src/utils/defineSavedMetrics.ts b/superset-frontend/packages/superset-ui-chart-controls/src/utils/defineSavedMetrics.ts index 4f0732164cd84..431b6cb4be3d0 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/src/utils/defineSavedMetrics.ts +++ b/superset-frontend/packages/superset-ui-chart-controls/src/utils/defineSavedMetrics.ts @@ -18,9 +18,8 @@ * under the License. */ -import { QueryResponse } from '@superset-ui/core'; +import { QueryResponse, DEFAULT_METRICS } from '@superset-ui/core'; import { Dataset } from '../types'; -import { DEFAULT_METRICS } from '..'; export const defineSavedMetrics = ( datasource: Dataset | QueryResponse | null, diff --git a/superset-frontend/packages/superset-ui-chart-controls/test/utils/defineSavedMetrics.test.tsx b/superset-frontend/packages/superset-ui-chart-controls/test/utils/defineSavedMetrics.test.tsx new file mode 100644 index 0000000000000..755ba8b5a0b04 --- /dev/null +++ b/superset-frontend/packages/superset-ui-chart-controls/test/utils/defineSavedMetrics.test.tsx @@ -0,0 +1,54 @@ +import { + DatasourceType, + DEFAULT_METRICS, + QueryResponse, + testQuery, +} from '@superset-ui/core'; +import { defineSavedMetrics } from '@superset-ui/chart-controls'; + +describe('defineSavedMetrics', () => { + it('defines saved metrics if source is a Dataset', () => { + expect( + defineSavedMetrics({ + id: 1, + metrics: [ + { + metric_name: 'COUNT(*) non-default-dataset-metric', + expression: 'COUNT(*) non-default-dataset-metric', + }, + ], + type: DatasourceType.Table, + main_dttm_col: 'test', + time_grain_sqla: 'P1D', + columns: [ + { + column_name: 'fiz', + }, + { + column_name: 'about', + verbose_name: 'right', + }, + { + column_name: 'foo', + verbose_name: 'bar', + }, + ], + verbose_map: {}, + column_format: { fiz: 'NUMERIC', about: 'STRING', foo: 'DATE' }, + datasource_name: 'my_datasource', + description: 'this is my datasource', + }), + ).toEqual([ + { + metric_name: 'COUNT(*) non-default-dataset-metric', + expression: 'COUNT(*) non-default-dataset-metric', + }, + ]); + }); + + it('returns default saved metrics if souce is a Query', () => { + expect(defineSavedMetrics(testQuery as QueryResponse)).toEqual( + DEFAULT_METRICS, + ); + }); +}); diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/Datasource.ts b/superset-frontend/packages/superset-ui-core/src/query/types/Datasource.ts index 9e1c3caadfc99..1bb4e7b991b73 100644 --- a/superset-frontend/packages/superset-ui-core/src/query/types/Datasource.ts +++ b/superset-frontend/packages/superset-ui-core/src/query/types/Datasource.ts @@ -47,4 +47,11 @@ export interface Datasource { }; } +export const DEFAULT_METRICS = [ + { + metric_name: 'COUNT(*)', + expression: 'COUNT(*)', + }, +]; + export default {}; From 054ee9744961a3f5bbf58c5bf7a6d1d3ae7a0da7 Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Tue, 7 Jun 2022 12:20:20 -0500 Subject: [PATCH 56/57] Add license and clean dataset test object --- .../test/utils/defineSavedMetrics.test.tsx | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/superset-frontend/packages/superset-ui-chart-controls/test/utils/defineSavedMetrics.test.tsx b/superset-frontend/packages/superset-ui-chart-controls/test/utils/defineSavedMetrics.test.tsx index 755ba8b5a0b04..59036bf60495d 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/test/utils/defineSavedMetrics.test.tsx +++ b/superset-frontend/packages/superset-ui-chart-controls/test/utils/defineSavedMetrics.test.tsx @@ -1,3 +1,21 @@ +/** + * 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 { DatasourceType, DEFAULT_METRICS, @@ -20,21 +38,9 @@ describe('defineSavedMetrics', () => { type: DatasourceType.Table, main_dttm_col: 'test', time_grain_sqla: 'P1D', - columns: [ - { - column_name: 'fiz', - }, - { - column_name: 'about', - verbose_name: 'right', - }, - { - column_name: 'foo', - verbose_name: 'bar', - }, - ], + columns: [], verbose_map: {}, - column_format: { fiz: 'NUMERIC', about: 'STRING', foo: 'DATE' }, + column_format: {}, datasource_name: 'my_datasource', description: 'this is my datasource', }), From 55143057f3be49895697df4b23fbe7154d34dc4b Mon Sep 17 00:00:00 2001 From: lyndsiWilliams Date: Tue, 7 Jun 2022 14:11:53 -0500 Subject: [PATCH 57/57] Change DatasourceType.Dataset to dataset --- .../packages/superset-ui-core/src/query/types/Datasource.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/Datasource.ts b/superset-frontend/packages/superset-ui-core/src/query/types/Datasource.ts index 1bb4e7b991b73..03916dee5ebb6 100644 --- a/superset-frontend/packages/superset-ui-core/src/query/types/Datasource.ts +++ b/superset-frontend/packages/superset-ui-core/src/query/types/Datasource.ts @@ -23,7 +23,7 @@ export enum DatasourceType { Table = 'table', Druid = 'druid', Query = 'query', - Dataset = 'sl_dataset', + Dataset = 'dataset', SlTable = 'sl_table', SavedQuery = 'saved_query', }