Skip to content

Commit

Permalink
feat(explore): Frontend implementation of dataset creation from infob…
Browse files Browse the repository at this point in the history
…ox (#19855)

* Frontend implementation of create dataset from infobox

* Fixed sl_dataset type

* Fix test

* Fixed sl_dataset type (forgot to save)

* RTL testing

* Adjusted styling/text on infobox and save dataset modal

* Appease lint

* Make infobox invisible and fix tests

* Remove unnecessary placeholder

* Move types to sql lab

* Moved logic into save dataset modal

* Change DatasourceMeta type to Dataset

* Add ExploreDatasource union type to save dataset modal

* Get user info from redux inside save dataset modal

* Addressed comments

* Adjusting to new query type

* Fixed save dataset in explore and union type

* Added testing

* Defined d for queries

* Remove dataset from SaveDatasetModal

* Clarify useSelector parameter

* Fix dndControls union type

* Fix shared-controls union type

* Fix controlPanel union type

* Move ExploreRootState to explore type file

* Remove unnecessary testing playground

* Move datasource type check in DatasourcePanel to a function

* Make all sqllab Query imports reference @superset-ui/core Query type

* Deconstruct query props in ResultSet

* Fix union type in /legacy-plugin-chart-heatmap/src/controlPanel

* Change SaveDatasetModal tests to RTL

* Cleaned datasourceTypeCheck

* Fix infobox styling

* Fix SaveDatasetModal test

* Fix query fixture in sqllab and Query type in SaveDatasetModal test

* Fix Query type and make test query fixture

* Added columns to Query type, separated results property, created QueryResponse union type, and fixed all types affected

* Fixed a couple missed broken types

* Added ExploreDatasource to SqlLab type file

* Removed unneeded Query import from DatasourcePanel

* Address PR comments

* Fix columnChoices

* Fix all incorrect column property checks

* Fix logic on dndGroupByControl

* Dry up savedMetrics type check

* Fixed TIME_COLUMN_OPTION

* Dried savedMetrics type check even further

* Change savedMetricsTypeCheck to defineSavedMetrics

* Change datasourceTypeCheck to isValidDatasourceType

* Fix Query path in groupByControl

* dnd_granularity_sqla now sorts Query types with is_dttm at the top

* Fixed/cleaned query sort

* Add sortedQueryColumns and proper optional chaining to granularity_sqla

* Move testQuery to core-ui, add test coverage for Queries in columnChoices

* Moved DEFAULT_METRICS to core-ui and wrote a test for defineSavedMetrics

* Add license and clean dataset test object

* Change DatasourceType.Dataset to dataset
  • Loading branch information
lyndsiWilliams authored Jun 7, 2022
1 parent d1c24f8 commit ba0c37d
Show file tree
Hide file tree
Showing 40 changed files with 1,123 additions and 683 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -32,7 +39,7 @@ export const COLUMN_NAME_ALIASES: Record<string, string> = {
[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,
Expand All @@ -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'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@
import {
FeatureFlag,
isFeatureEnabled,
QueryColumn,
QueryResponse,
t,
validateNonEmpty,
} from '@superset-ui/core';
import { ExtraControlProps, SharedControlConfig } from '../types';
import { TIME_COLUMN_OPTION, TIME_FILTER_LABELS } from '../constants';
import { ExtraControlProps, SharedControlConfig, Dataset } from '../types';
import { DATASET_TIME_COLUMN_OPTION, TIME_FILTER_LABELS } from '../constants';
import { QUERY_TIME_COLUMN_OPTION, defineSavedMetrics } from '..';

export const dndGroupByControl: SharedControlConfig<'DndColumnSelect'> = {
type: 'DndColumnSelect',
Expand All @@ -36,15 +39,25 @@ export const dndGroupByControl: SharedControlConfig<'DndColumnSelect'> = {
),
mapStateToProps(state, { includeTime }) {
const newState: ExtraControlProps = {};
if (state.datasource) {
const options = state.datasource.columns.filter(c => c.groupby);
const { datasource } = state;
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 = state.datasource.metrics || [];
newState.savedMetrics = (datasource as Dataset).metrics || [];
} 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;
},
Expand Down Expand Up @@ -83,8 +96,10 @@ export const dnd_adhoc_filters: SharedControlConfig<'DndFilterSelect'> = {
default: [],
description: '',
mapStateToProps: ({ datasource, form_data }) => ({
columns: datasource?.columns.filter(c => c.filterable) || [],
savedMetrics: datasource?.metrics || [],
columns: datasource?.columns[0]?.hasOwnProperty('filterable')
? (datasource as Dataset)?.columns.filter(c => c.filterable)
: datasource?.columns || [],
savedMetrics: defineSavedMetrics(datasource),
// current active adhoc metrics
selectedMetrics:
form_data.metrics || (form_data.metric ? [form_data.metric] : []),
Expand All @@ -99,8 +114,8 @@ export const dnd_adhoc_metrics: SharedControlConfig<'DndMetricSelect'> = {
label: t('Metrics'),
validators: [validateNonEmpty],
mapStateToProps: ({ datasource }) => ({
columns: datasource ? datasource.columns : [],
savedMetrics: datasource ? datasource.metrics : [],
columns: datasource?.columns || [],
savedMetrics: defineSavedMetrics(datasource),
datasource,
datasourceType: datasource?.type,
}),
Expand Down Expand Up @@ -130,7 +145,7 @@ export const dnd_sort_by: SharedControlConfig<'DndMetricSelect'> = {
),
mapStateToProps: ({ datasource }) => ({
columns: datasource?.columns || [],
savedMetrics: datasource?.metrics || [],
savedMetrics: defineSavedMetrics(datasource),
datasource,
datasourceType: datasource?.type,
}),
Expand Down Expand Up @@ -178,14 +193,31 @@ export const dnd_granularity_sqla: typeof dndGroupByControl = {
: 'Drop temporal column here',
),
mapStateToProps: ({ datasource }) => {
const temporalColumns = datasource?.columns.filter(c => c.is_dttm) ?? [];
if (datasource?.columns[0]?.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 sortedQueryColumns = (datasource as QueryResponse)?.columns?.sort(
query => (query?.is_dttm ? -1 : 1),
);
const options = Object.fromEntries(
temporalColumns.map(option => [option.column_name, option]),
sortedQueryColumns.map(option => [option.name, option]),
);
return {
options,
default:
datasource?.main_dttm_col || temporalColumns[0]?.column_name || null,
default: sortedQueryColumns[0]?.name || null,
isTemporal: true,
};
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ import {
legacyValidateInteger,
validateNonEmpty,
ComparisionType,
QueryResponse,
QueryColumn,
} from '@superset-ui/core';

import {
Expand All @@ -55,14 +57,16 @@ import {
D3_TIME_FORMAT_DOCS,
DEFAULT_TIME_FORMAT,
DEFAULT_NUMBER_FORMAT,
defineSavedMetrics,
} from '../utils';
import { TIME_FILTER_LABELS, TIME_COLUMN_OPTION } from '../constants';
import { TIME_FILTER_LABELS, DATASET_TIME_COLUMN_OPTION } from '../constants';
import {
Metric,
SharedControlConfig,
ColumnMeta,
ExtraControlProps,
SelectControlConfig,
Dataset,
} from '../types';
import { ColumnOption } from '../components/ColumnOption';

Expand All @@ -82,6 +86,7 @@ import {
dndSeries,
dnd_adhoc_metric_2,
} from './dndControls';
import { QUERY_TIME_COLUMN_OPTION } from '..';

const categoricalSchemeRegistry = getCategoricalSchemeRegistry();
const sequentialSchemeRegistry = getSequentialSchemeRegistry();
Expand Down Expand Up @@ -131,11 +136,14 @@ const groupByControl: SharedControlConfig<'SelectControl', ColumnMeta> = {
promptTextCreator: (label: unknown) => label,
mapStateToProps(state, { includeTime }) {
const newState: ExtraControlProps = {};
if (state.datasource) {
const options = state.datasource.columns.filter(c => c.groupby);
if (includeTime) {
options.unshift(TIME_COLUMN_OPTION);
}
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);
newState.options = options;
} else {
const options = (datasource as QueryResponse).columns;
if (includeTime) options.unshift(QUERY_TIME_COLUMN_OPTION);
newState.options = options;
}
return newState;
Expand All @@ -149,8 +157,8 @@ const metrics: SharedControlConfig<'MetricsControl'> = {
label: t('Metrics'),
validators: [validateNonEmpty],
mapStateToProps: ({ datasource }) => ({
columns: datasource ? datasource.columns : [],
savedMetrics: datasource ? datasource.metrics : [],
columns: datasource?.columns || [],
savedMetrics: defineSavedMetrics(datasource),
datasource,
datasourceType: datasource?.type,
}),
Expand Down Expand Up @@ -292,15 +300,23 @@ const granularity_sqla: SharedControlConfig<'SelectControl', ColumnMeta> = {
valueRenderer: c => <ColumnOption column={c} />,
valueKey: 'column_name',
mapStateToProps: state => {
const props: Partial<SelectControlConfig<ColumnMeta>> = {};
if (state.datasource) {
props.options = state.datasource.columns.filter(c => c.is_dttm);
const props: Partial<SelectControlConfig<ColumnMeta | QueryColumn>> = {};
const { datasource } = state;
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 (state.datasource.main_dttm_col) {
props.default = state.datasource.main_dttm_col;
} else if (props.options && props.options.length > 0) {
props.default = props.options[0].column_name;
if (dataset.main_dttm_col) {
props.default = dataset.main_dttm_col;
} else if (props?.options) {
props.default = (props.options[0] as ColumnMeta).column_name;
}
} else {
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;
},
Expand All @@ -318,7 +334,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,
}),
};

Expand All @@ -335,7 +351,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,
}),
};
Expand Down Expand Up @@ -401,7 +417,7 @@ const sort_by: SharedControlConfig<'MetricsControl'> = {
),
mapStateToProps: ({ datasource }) => ({
columns: datasource?.columns || [],
savedMetrics: datasource?.metrics || [],
savedMetrics: defineSavedMetrics(datasource),
datasource,
datasourceType: datasource?.type,
}),
Expand Down Expand Up @@ -493,8 +509,10 @@ const adhoc_filters: SharedControlConfig<'AdhocFilterControl'> = {
default: [],
description: '',
mapStateToProps: ({ datasource, form_data }) => ({
columns: datasource?.columns.filter(c => c.filterable) || [],
savedMetrics: datasource?.metrics || [],
columns: datasource?.columns[0]?.hasOwnProperty('filterable')
? (datasource as Dataset)?.columns.filter(c => c.filterable)
: datasource?.columns || [],
savedMetrics: defineSavedMetrics(datasource),
// current active adhoc metrics
selectedMetrics:
form_data.metrics || (form_data.metric ? [form_data.metric] : []),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import type {
JsonValue,
Metric,
QueryFormData,
QueryResponse,
QueryFormMetric,
QueryFormColumn,
} from '@superset-ui/core';
Expand Down Expand Up @@ -53,7 +54,7 @@ export type ColumnMeta = Omit<Column, 'id'> & {
id?: number;
} & AnyDict;

export interface DatasourceMeta {
export interface Dataset {
id: number;
type: DatasourceType;
columns: ColumnMeta[];
Expand All @@ -71,7 +72,7 @@ export interface DatasourceMeta {

export interface ControlPanelState {
form_data: QueryFormData;
datasource: DatasourceMeta | null;
datasource: Dataset | QueryResponse | null;
controls: ControlStateMapping;
}

Expand All @@ -90,7 +91,7 @@ export interface ActionDispatcher<
* Mapping of action dispatchers
*/
export interface ControlPanelActionDispatchers {
setDatasource: ActionDispatcher<[DatasourceMeta]>;
setDatasource: ActionDispatcher<[Dataset]>;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,30 @@
* specific language governing permissions and limitations
* under the License.
*/
import { DatasourceMeta } from '../types';
import { QueryResponse } from '@superset-ui/core';
import { Dataset } from '../types';

/**
* Convert Datasource columns to column choices
*/
export default function columnChoices(
datasource?: DatasourceMeta | null,
datasource?: Dataset | QueryResponse | null,
): [string, string][] {
if (datasource?.columns[0]?.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,
) || []
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/* 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, DEFAULT_METRICS } from '@superset-ui/core';
import { Dataset } from '../types';

export const defineSavedMetrics = (
datasource: Dataset | QueryResponse | null,
) =>
datasource?.hasOwnProperty('metrics')
? (datasource as Dataset)?.metrics || []
: DEFAULT_METRICS;
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ export * from './expandControlConfig';
export * from './getColorFormatters';
export { default as mainMetric } from './mainMetric';
export { default as columnChoices } from './columnChoices';
export * from './defineSavedMetrics';
Loading

0 comments on commit ba0c37d

Please sign in to comment.