| /** |
| * 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, |
| VizType, |
| } from '@superset-ui/core'; |
| // TODO: tests shouldn't depend on plugins |
| import { sections } from '@superset-ui/chart-controls'; |
| import TableChartPlugin from '../../../plugins/plugin-chart-table/src'; |
| import { BigNumberTotalChartPlugin } from '../../../plugins/plugin-chart-echarts/src'; |
| 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: VizType.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 as keyof typeof publicControlsFormData], |
| ); |
| }); |
| 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( |
| VizType.BigNumberTotal, |
| 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( |
| VizType.BigNumberTotal, |
| tableVizStore, |
| ); |
| |
| // bigNumberTotal -> table |
| const sfd2 = new StandardizedFormData(bntFormData); |
| expect(sfd2.has(VizType.BigNumberTotal)).toBeTruthy(); |
| expect(sfd2.has('table')).toBeTruthy(); |
| expect(sfd2.get(VizType.BigNumberTotal).viz_type).toBe( |
| VizType.BigNumberTotal, |
| ); |
| expect(sfd2.get('table').viz_type).toBe(VizType.Table); |
| }); |
| |
| test('transform', () => { |
| // table -> bigNumberTotal |
| const sfd = new StandardizedFormData(tableVizFormData); |
| const { formData: bntFormData, controlsState: bntControlsState } = |
| sfd.transform(VizType.BigNumberTotal, tableVizStore); |
| expect(Object.keys(bntFormData).sort()).toEqual( |
| [...Object.keys(bntControlsState), 'standardizedFormData'].sort(), |
| ); |
| expect(bntFormData.viz_type).toBe(VizType.BigNumberTotal); |
| expect(bntFormData.metric).toBe('count'); |
| |
| // change control values on bigNumber |
| bntFormData.metric = 'sum(sales)'; |
| |
| bntControlsState.metric.value = 'sum(sales)'; |
| |
| // 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(VizType.Table); |
| expect(tblFormData.metrics).toEqual([ |
| 'sum(sales)', |
| 'avg(sales)', |
| adhocMetricSimple, |
| adhocMetricSQL, |
| ]); |
| expect(tblFormData.groupby).toEqual(['name', 'gender', adhocColumn]); |
| }); |
| }); |
| |
| describe('initial SFD between different datasource', () => { |
| beforeAll(() => { |
| getChartControlPanelRegistry().registerValue( |
| VizType.BigNumberTotal, |
| 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(VizType.BigNumberTotal, 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([VizType.Table, VizType.BigNumberTotal]); |
| const newDatasourceFormData = { ...tblFormData, datasource: '20__table' }; |
| const newDatasourceSFD = new StandardizedFormData(newDatasourceFormData); |
| expect( |
| newDatasourceSFD |
| .serialize() |
| .memorizedFormData.map(([vizType]) => vizType), |
| ).toEqual([VizType.Table]); |
| expect(newDatasourceSFD.get('table')).not.toHaveProperty( |
| 'standardizedFormData', |
| ); |
| }); |
| }); |