mirror of
https://github.com/apache/superset.git
synced 2026-06-08 09:09:27 +00:00
488 lines
13 KiB
TypeScript
488 lines
13 KiB
TypeScript
/**
|
|
* 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 {
|
|
AdhocColumn,
|
|
AdhocMetricSimple,
|
|
AdhocMetricSQL,
|
|
getChartControlPanelRegistry,
|
|
QueryFormData,
|
|
TimeGranularity,
|
|
} from '@superset-ui/core';
|
|
import TableChartPlugin from '@superset-ui/plugin-chart-table';
|
|
import { BigNumberTotalChartPlugin } from '@superset-ui/plugin-chart-echarts';
|
|
import { sections } from '@superset-ui/chart-controls';
|
|
import {
|
|
StandardizedFormData,
|
|
sharedMetricsKey,
|
|
sharedColumnsKey,
|
|
publicControls,
|
|
} from './standardizedFormData';
|
|
|
|
const adhocColumn: AdhocColumn = {
|
|
expressionType: 'SQL',
|
|
label: 'country',
|
|
optionName: 'country',
|
|
sqlExpression: 'country',
|
|
};
|
|
const adhocMetricSQL: AdhocMetricSQL = {
|
|
expressionType: 'SQL',
|
|
label: 'count',
|
|
optionName: 'count',
|
|
sqlExpression: 'count(*)',
|
|
};
|
|
const adhocMetricSimple: AdhocMetricSimple = {
|
|
expressionType: 'SIMPLE',
|
|
column: {
|
|
id: 1,
|
|
column_name: 'sales',
|
|
columnName: 'sales',
|
|
verbose_name: 'sales',
|
|
},
|
|
aggregate: 'SUM',
|
|
label: 'count',
|
|
optionName: 'count',
|
|
};
|
|
|
|
const tableVizFormData = {
|
|
datasource: '30__table',
|
|
viz_type: 'table',
|
|
granularity_sqla: 'ds',
|
|
time_grain_sqla: TimeGranularity.DAY,
|
|
time_range: 'No filter',
|
|
query_mode: 'aggregate',
|
|
groupby: ['name', 'gender', adhocColumn],
|
|
metrics: ['count', 'avg(sales)', adhocMetricSimple, adhocMetricSQL],
|
|
all_columns: [],
|
|
percent_metrics: [],
|
|
adhoc_filters: [],
|
|
order_by_cols: [],
|
|
row_limit: 10000,
|
|
server_page_length: 10,
|
|
order_desc: true,
|
|
table_timestamp_format: 'smart_date',
|
|
show_cell_bars: true,
|
|
color_pn: true,
|
|
url_params: {
|
|
form_data_key:
|
|
'p3No_sqDW7k-kMTzlBPAPd9vwp1IXTf6stbyzjlrPPa0ninvdYUUiMC6F1iKit3Y',
|
|
dataset_id: '30',
|
|
},
|
|
};
|
|
const tableVizStore = {
|
|
form_data: tableVizFormData,
|
|
controls: {
|
|
datasource: {
|
|
value: '30__table',
|
|
},
|
|
viz_type: {
|
|
value: 'table',
|
|
},
|
|
slice_id: {},
|
|
cache_timeout: {},
|
|
url_params: {
|
|
value: {
|
|
form_data_key:
|
|
'p3No_sqDW7k-kMTzlBPAPd9vwp1IXTf6stbyzjlrPPa0ninvdYUUiMC6F1iKit3Y',
|
|
dataset_id: '30',
|
|
},
|
|
},
|
|
granularity_sqla: {
|
|
value: 'ds',
|
|
},
|
|
time_grain_sqla: {
|
|
value: 'P1D',
|
|
},
|
|
time_range: {
|
|
value: 'No filter',
|
|
},
|
|
query_mode: {
|
|
value: 'aggregate',
|
|
},
|
|
groupby: {
|
|
value: ['name', 'gender', adhocColumn],
|
|
},
|
|
metrics: {
|
|
value: ['count', 'avg(sales)', adhocMetricSimple, adhocMetricSQL],
|
|
},
|
|
all_columns: {
|
|
value: [],
|
|
},
|
|
percent_metrics: {
|
|
value: [],
|
|
},
|
|
adhoc_filters: {
|
|
value: [],
|
|
},
|
|
timeseries_limit_metric: {},
|
|
order_by_cols: {
|
|
value: [],
|
|
},
|
|
server_pagination: {},
|
|
row_limit: {
|
|
value: 10000,
|
|
},
|
|
server_page_length: {
|
|
value: 10,
|
|
},
|
|
include_time: {},
|
|
order_desc: {
|
|
value: true,
|
|
},
|
|
show_totals: {},
|
|
table_timestamp_format: {
|
|
value: 'smart_date',
|
|
},
|
|
page_length: {},
|
|
include_search: {},
|
|
show_cell_bars: {
|
|
value: true,
|
|
},
|
|
align_pn: {},
|
|
color_pn: {
|
|
value: true,
|
|
},
|
|
column_config: {},
|
|
conditional_formatting: {},
|
|
},
|
|
datasource: {
|
|
type: 'table',
|
|
columns: [],
|
|
},
|
|
};
|
|
|
|
describe('should collect control values and create SFD', () => {
|
|
const sharedKey = [...sharedMetricsKey, ...sharedColumnsKey];
|
|
const sharedControlsFormData = {
|
|
// metrics
|
|
metric: 'm1',
|
|
metrics: ['m2'],
|
|
metric_2: 'm3',
|
|
size: 'm4',
|
|
x: 'm5',
|
|
y: 'm6',
|
|
secondary_metric: 'm7',
|
|
// columns
|
|
groupby: ['c1'],
|
|
columns: ['c2'],
|
|
groupbyColumns: ['c3'],
|
|
groupbyRows: ['c4'],
|
|
series: 'c5',
|
|
entity: 'c6',
|
|
series_columns: ['c7'],
|
|
};
|
|
const publicControlsFormData = {
|
|
// time section
|
|
granularity_sqla: 'time_column',
|
|
time_grain_sqla: TimeGranularity.DAY,
|
|
time_range: '2000 : today',
|
|
// filters
|
|
adhoc_filters: [],
|
|
// subquery limit(series limit)
|
|
limit: 5,
|
|
// order by clause
|
|
timeseries_limit_metric: 'orderby_metric',
|
|
series_limit_metric: 'orderby_metric',
|
|
// desc or asc in order by clause
|
|
order_desc: true,
|
|
// outer query limit
|
|
row_limit: 100,
|
|
// x asxs column
|
|
x_axis: 'x_axis_column',
|
|
// advanced analytics - rolling window
|
|
rolling_type: 'sum',
|
|
rolling_periods: 1,
|
|
min_periods: 0,
|
|
// advanced analytics - time comparison
|
|
time_compare: '1 year ago',
|
|
comparison_type: 'values',
|
|
// advanced analytics - resample
|
|
resample_rule: '1D',
|
|
resample_method: 'zerofill',
|
|
};
|
|
const sourceMockFormData: QueryFormData = {
|
|
...sharedControlsFormData,
|
|
...publicControlsFormData,
|
|
datasource: '100__table',
|
|
viz_type: 'source_viz',
|
|
};
|
|
|
|
const sourceMockStore = {
|
|
form_data: sourceMockFormData,
|
|
controls: Object.fromEntries(
|
|
Object.entries(sourceMockFormData).map(([key, value]) => [
|
|
key,
|
|
{ value },
|
|
]),
|
|
),
|
|
datasource: {
|
|
type: 'table',
|
|
columns: [],
|
|
},
|
|
};
|
|
|
|
beforeAll(() => {
|
|
getChartControlPanelRegistry().registerValue('source_viz', {
|
|
controlPanelSections: [
|
|
sections.advancedAnalyticsControls,
|
|
{
|
|
label: 'transform controls',
|
|
controlSetRows: publicControls.map(control => [control]),
|
|
},
|
|
{
|
|
label: 'axis column',
|
|
controlSetRows: [['x_axis']],
|
|
},
|
|
],
|
|
});
|
|
getChartControlPanelRegistry().registerValue('target_viz', {
|
|
controlPanelSections: [
|
|
sections.advancedAnalyticsControls,
|
|
{
|
|
label: 'transform controls',
|
|
controlSetRows: publicControls.map(control => [control]),
|
|
},
|
|
{
|
|
label: 'axis column',
|
|
controlSetRows: [['x_axis']],
|
|
},
|
|
],
|
|
formDataOverrides: (formData: QueryFormData) => ({
|
|
...formData,
|
|
columns: formData.standardizedFormData.controls.columns,
|
|
metrics: formData.standardizedFormData.controls.metrics,
|
|
}),
|
|
});
|
|
});
|
|
|
|
test('should avoid to overlap', () => {
|
|
const sharedControlsSet = new Set(Object.keys(sharedKey));
|
|
const publicControlsSet = new Set(publicControls);
|
|
expect(
|
|
[...sharedControlsSet].filter((x: string) => publicControlsSet.has(x)),
|
|
).toEqual([]);
|
|
});
|
|
|
|
test('should collect all sharedControls', () => {
|
|
expect(Object.entries(sharedControlsFormData).length).toBe(
|
|
Object.entries(sharedKey).length,
|
|
);
|
|
const sfd = new StandardizedFormData(sourceMockFormData);
|
|
expect(sfd.serialize().controls.metrics).toEqual([
|
|
'm1',
|
|
'm2',
|
|
'm3',
|
|
'm4',
|
|
'm5',
|
|
'm6',
|
|
'm7',
|
|
]);
|
|
expect(sfd.serialize().controls.columns).toEqual([
|
|
'c1',
|
|
'c2',
|
|
'c3',
|
|
'c4',
|
|
'c5',
|
|
'c6',
|
|
'c7',
|
|
]);
|
|
});
|
|
|
|
test('should transform all publicControls and sharedControls', () => {
|
|
expect(Object.entries(publicControlsFormData).length).toBe(
|
|
publicControls.length,
|
|
);
|
|
|
|
const sfd = new StandardizedFormData(sourceMockFormData);
|
|
const { formData } = sfd.transform('target_viz', sourceMockStore);
|
|
Object.entries(publicControlsFormData).forEach(([key, value]) => {
|
|
expect(formData).toHaveProperty(key);
|
|
expect(value).toEqual(publicControlsFormData[key]);
|
|
});
|
|
expect(formData.columns).toEqual([
|
|
'c1',
|
|
'c2',
|
|
'c3',
|
|
'c4',
|
|
'c5',
|
|
'c6',
|
|
'c7',
|
|
]);
|
|
expect(formData.metrics).toEqual([
|
|
'm1',
|
|
'm2',
|
|
'm3',
|
|
'm4',
|
|
'm5',
|
|
'm6',
|
|
'm7',
|
|
]);
|
|
});
|
|
|
|
test('should inherit standardizedFormData and memorizedFormData is LIFO', () => {
|
|
// from source_viz to target_viz
|
|
const sfd = new StandardizedFormData(sourceMockFormData);
|
|
const { formData, controlsState } = sfd.transform(
|
|
'target_viz',
|
|
sourceMockStore,
|
|
);
|
|
expect(
|
|
formData.standardizedFormData.memorizedFormData.map(
|
|
(fd: [string, QueryFormData]) => fd[0],
|
|
),
|
|
).toEqual(['source_viz']);
|
|
|
|
// from target_viz to source_viz
|
|
const sfd2 = new StandardizedFormData(formData);
|
|
const { formData: fd2, controlsState: cs2 } = sfd2.transform('source_viz', {
|
|
...sourceMockStore,
|
|
form_data: formData,
|
|
controls: controlsState,
|
|
});
|
|
expect(
|
|
fd2.standardizedFormData.memorizedFormData.map(
|
|
(fd: [string, QueryFormData]) => fd[0],
|
|
),
|
|
).toEqual(['source_viz', 'target_viz']);
|
|
|
|
// from source_viz to target_viz
|
|
const sfd3 = new StandardizedFormData(fd2);
|
|
const { formData: fd3 } = sfd3.transform('target_viz', {
|
|
...sourceMockStore,
|
|
form_data: fd2,
|
|
controls: cs2,
|
|
});
|
|
expect(
|
|
fd3.standardizedFormData.memorizedFormData.map(
|
|
(fd: [string, QueryFormData]) => fd[0],
|
|
),
|
|
).toEqual(['target_viz', 'source_viz']);
|
|
});
|
|
});
|
|
|
|
describe('should transform form_data between table and bigNumberTotal', () => {
|
|
beforeAll(() => {
|
|
getChartControlPanelRegistry().registerValue(
|
|
'big_number_total',
|
|
new BigNumberTotalChartPlugin().controlPanel,
|
|
);
|
|
getChartControlPanelRegistry().registerValue(
|
|
'table',
|
|
new TableChartPlugin().controlPanel,
|
|
);
|
|
});
|
|
|
|
test('get and has', () => {
|
|
// table -> bigNumberTotal
|
|
const sfd = new StandardizedFormData(tableVizFormData);
|
|
const { formData: bntFormData } = sfd.transform(
|
|
'big_number_total',
|
|
tableVizStore,
|
|
);
|
|
|
|
// bigNumberTotal -> table
|
|
const sfd2 = new StandardizedFormData(bntFormData);
|
|
expect(sfd2.has('big_number_total')).toBeTruthy();
|
|
expect(sfd2.has('table')).toBeTruthy();
|
|
expect(sfd2.get('big_number_total').viz_type).toBe('big_number_total');
|
|
expect(sfd2.get('table').viz_type).toBe('table');
|
|
});
|
|
|
|
test('transform', () => {
|
|
// table -> bigNumberTotal
|
|
const sfd = new StandardizedFormData(tableVizFormData);
|
|
const { formData: bntFormData, controlsState: bntControlsState } =
|
|
sfd.transform('big_number_total', tableVizStore);
|
|
expect(Object.keys(bntFormData).sort()).toEqual(
|
|
[...Object.keys(bntControlsState), 'standardizedFormData'].sort(),
|
|
);
|
|
expect(bntFormData.viz_type).toBe('big_number_total');
|
|
expect(bntFormData.metric).toBe('count');
|
|
|
|
// change control values on bigNumber
|
|
bntFormData.metric = 'sum(sales)';
|
|
bntFormData.time_range = '2021 : 2022';
|
|
bntControlsState.metric.value = 'sum(sales)';
|
|
bntControlsState.time_range.value = '2021 : 2022';
|
|
|
|
// bigNumberTotal -> table
|
|
const sfd2 = new StandardizedFormData(bntFormData);
|
|
const { formData: tblFormData, controlsState: tblControlsState } =
|
|
sfd2.transform('table', {
|
|
...tableVizStore,
|
|
form_data: bntFormData,
|
|
controls: bntControlsState,
|
|
});
|
|
expect(Object.keys(tblFormData).sort()).toEqual(
|
|
[...Object.keys(tblControlsState), 'standardizedFormData'].sort(),
|
|
);
|
|
expect(tblFormData.viz_type).toBe('table');
|
|
expect(tblFormData.metrics).toEqual([
|
|
'sum(sales)',
|
|
'avg(sales)',
|
|
adhocMetricSimple,
|
|
adhocMetricSQL,
|
|
]);
|
|
expect(tblFormData.groupby).toEqual(['name', 'gender', adhocColumn]);
|
|
expect(tblFormData.time_range).toBe('2021 : 2022');
|
|
});
|
|
});
|
|
|
|
describe('initial SFD between different datasource', () => {
|
|
beforeAll(() => {
|
|
getChartControlPanelRegistry().registerValue(
|
|
'big_number_total',
|
|
new BigNumberTotalChartPlugin().controlPanel,
|
|
);
|
|
getChartControlPanelRegistry().registerValue(
|
|
'table',
|
|
new TableChartPlugin().controlPanel,
|
|
);
|
|
});
|
|
|
|
test('initial SFD between different datasource', () => {
|
|
const sfd = new StandardizedFormData(tableVizFormData);
|
|
// table -> big number
|
|
const { formData: bntFormData, controlsState: bntControlsState } =
|
|
sfd.transform('big_number_total', tableVizStore);
|
|
const sfd2 = new StandardizedFormData(bntFormData);
|
|
// big number -> table
|
|
const { formData: tblFormData } = sfd2.transform('table', {
|
|
...tableVizStore,
|
|
form_data: bntFormData,
|
|
controls: bntControlsState,
|
|
});
|
|
|
|
expect(
|
|
tblFormData.standardizedFormData.memorizedFormData.map(
|
|
(mfd: [string, QueryFormData][]) => mfd[0],
|
|
),
|
|
).toEqual(['table', 'big_number_total']);
|
|
const newDatasourceFormData = { ...tblFormData, datasource: '20__table' };
|
|
const newDatasourceSFD = new StandardizedFormData(newDatasourceFormData);
|
|
expect(
|
|
newDatasourceSFD
|
|
.serialize()
|
|
.memorizedFormData.map(([vizType]) => vizType),
|
|
).toEqual(['table']);
|
|
expect(newDatasourceSFD.get('table')).not.toHaveProperty(
|
|
'standardizedFormData',
|
|
);
|
|
});
|
|
});
|