fix: use proper time grain when using native filters or filter box (#1104)

This commit is contained in:
Ville Brofeldt 2021-05-12 13:16:03 +03:00 committed by Yongjie Zhao
parent 379aeb8927
commit e51f034317
10 changed files with 157 additions and 12 deletions

View File

@ -0,0 +1,34 @@
/**
* 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.
*/
/* eslint-disable no-underscore-dangle */
import { QueryFormData } from './types';
import { TimeGranularity } from '../time-format';
export default function extractTimegrain(formData: QueryFormData): TimeGranularity | undefined {
const { time_grain_sqla, extra_filters, extra_form_data } = formData;
if (extra_form_data?.time_grain_sqla) {
return extra_form_data.time_grain_sqla;
}
const extra_grain = (extra_filters || []).filter(filter => filter.col === '__time_grain');
if (extra_grain.length) {
return extra_grain[0].val as TimeGranularity;
}
return time_grain_sqla;
}

View File

@ -23,6 +23,7 @@ export * from './constants';
export { default as buildQueryContext } from './buildQueryContext';
export { default as buildQueryObject } from './buildQueryObject';
export { default as convertFilter } from './convertFilter';
export { default as extractTimegrain } from './extractTimegrain';
export { default as getMetricLabel } from './getMetricLabel';
export { default as DatasourceKey } from './DatasourceKey';

View File

@ -1,5 +1,6 @@
/* eslint camelcase: 0 */
import { DataRecord } from './QueryResponse';
import { TimeGranularity } from '../../time-format';
export enum AnnotationType {
Event = 'EVENT',
@ -40,7 +41,7 @@ type BaseAnnotationLayer = {
type AnnotationOverrides = {
granularity?: string | null;
time_grain_sqla?: string | null;
time_grain_sqla?: TimeGranularity | null;
time_range?: string | null;
time_shift?: string | null;
};

View File

@ -25,6 +25,7 @@ import { QueryFields, QueryFormMetric } from './QueryFormData';
import { Maybe } from '../../types';
import { PostProcessingRule } from './PostProcessing';
import { JsonObject } from '../../connection';
import { TimeGranularity } from '../../time-format';
export type QueryObjectFilterClause = {
col: string;
@ -50,7 +51,7 @@ export type QueryObjectExtras = Partial<{
having?: string;
relative_start?: string;
relative_end?: string;
time_grain_sqla?: string;
time_grain_sqla?: TimeGranularity;
time_range_endpoints?: TimeRangeEndpoints;
/** WHERE condition */
where?: string;

View File

@ -0,0 +1,86 @@
/**
* 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 extractTimegrain from '@superset-ui/core/src/query/extractTimegrain';
import { QueryFormData } from '../../lib';
describe('extractTimegrain', () => {
const baseFormData: QueryFormData = {
datasource: 'table__1',
viz_type: 'my_viz',
};
it('should extract regular from form data', () => {
expect(
extractTimegrain({
...baseFormData,
time_grain_sqla: 'P1D',
}),
).toEqual('P1D');
});
it('should extract filter box time grain from form data', () => {
expect(
extractTimegrain({
...baseFormData,
time_grain_sqla: 'P1D',
extra_filters: [
{
col: '__time_grain',
op: '==',
val: 'P1M',
},
],
}),
).toEqual('P1M');
});
it('should extract native filter time grain from form data', () => {
expect(
extractTimegrain({
...baseFormData,
time_grain_sqla: 'P1D',
extra_form_data: {
time_grain_sqla: 'P1W',
},
}),
).toEqual('P1W');
});
it('should give priority to native filters', () => {
expect(
extractTimegrain({
...baseFormData,
time_grain_sqla: 'P1D',
extra_filters: [
{
col: '__time_grain',
op: '==',
val: 'P1M',
},
],
extra_form_data: {
time_grain_sqla: 'P1W',
},
}),
).toEqual('P1W');
});
it('returns undefined if timegrain not defined', () => {
expect(extractTimegrain({ ...baseFormData })).toEqual(undefined);
});
});

View File

@ -16,16 +16,19 @@
* specific language governing permissions and limitations
* under the License.
*/
import { extractTimegrain } from '@superset-ui/core';
export default function transformProps(chartProps) {
const { height, datasource, formData, queriesData } = chartProps;
const { timeGrainSqla, groupby, numberFormat, dateFormat } = formData;
const { height, datasource, formData, queriesData, rawFormData } = chartProps;
const { groupby, numberFormat, dateFormat } = formData;
const { columnFormats, verboseMap } = datasource;
const granularity = extractTimegrain(rawFormData);
return {
columnFormats,
data: queriesData[0].data,
dateFormat,
granularity: timeGrainSqla,
granularity,
height,
numberFormat,
numGroups: groupby.length,

View File

@ -18,7 +18,7 @@
*/
import * as color from 'd3-color';
import {
TimeGranularity,
extractTimegrain,
getTimeFormatterForGranularity,
getNumberFormatter,
NumberFormats,
@ -48,7 +48,6 @@ export type BigNumberFormData = QueryFormData & {
| string;
compareLag?: string | number;
yAxisFormat?: string;
timeGrainSqla?: TimeGranularity;
};
export type BigNumberChartProps = ChartProps & {
@ -59,7 +58,7 @@ export type BigNumberChartProps = ChartProps & {
};
export default function transformProps(chartProps: BigNumberChartProps) {
const { width, height, queriesData, formData } = chartProps;
const { width, height, queriesData, formData, rawFormData } = chartProps;
const {
colorPicker,
compareLag: compareLag_,
@ -70,10 +69,10 @@ export default function transformProps(chartProps: BigNumberChartProps) {
startYAxisAtZero,
subheader = '',
subheaderFontSize,
timeGrainSqla: granularity,
vizType,
timeRangeFixed = false,
} = formData;
const granularity = extractTimegrain(rawFormData as QueryFormData);
let { yAxisFormat } = formData;
const { data = [], from_dttm: fromDatetime, to_dttm: toDatetime } = queriesData[0];
const metricName = typeof metric === 'string' ? metric : metric.label;

View File

@ -37,6 +37,21 @@ const formData = {
yAxisFormat: '.3s',
};
const rawFormData = {
metric: 'value',
color_picker: {
r: 0,
g: 122,
b: 135,
a: 1,
},
compare_lag: 1,
time_grain_sqla: 'P0.25Y' as TimeGranularity,
compare_suffix: 'over last quarter',
viz_type: 'big_number',
y_axis_format: '.3s',
};
function generateProps(
data: BigNumberDatum[],
extraFormData = {},
@ -56,7 +71,7 @@ function generateProps(
verboseMap: {},
},
rawDatasource: {},
rawFormData: {},
rawFormData,
hooks: {},
initialValues: {},
formData: {

View File

@ -19,6 +19,7 @@
import memoizeOne from 'memoize-one';
import {
DataRecord,
extractTimegrain,
GenericDataType,
getMetricLabel,
getNumberFormatter,
@ -75,13 +76,13 @@ const processColumns = memoizeOne(function processColumns(props: TableChartProps
datasource: { columnFormats, verboseMap },
rawFormData: {
table_timestamp_format: tableTimestampFormat,
time_grain_sqla: granularity,
metrics: metrics_,
percent_metrics: percentMetrics_,
column_config: columnConfig = {},
},
queriesData,
} = props;
const granularity = extractTimegrain(props.rawFormData);
const { data: records, colnames, coltypes } = queriesData[0] || {};
// convert `metrics` and `percentMetrics` to the key names in `data.records`
const metrics = (metrics_ ?? []).map(getMetricLabel);

View File

@ -31,6 +31,10 @@ export default function isEqualColumns(propsA: TableChartProps[], propsB: TableC
JSON.stringify(b.formData.columnConfig || null) &&
isEqualArray(a.formData.metrics, b.formData.metrics) &&
isEqualArray(a.queriesData?.[0]?.colnames, b.queriesData?.[0]?.colnames) &&
isEqualArray(a.queriesData?.[0]?.coltypes, b.queriesData?.[0]?.coltypes)
isEqualArray(a.queriesData?.[0]?.coltypes, b.queriesData?.[0]?.coltypes) &&
JSON.stringify(a.formData.extraFilters || null) ===
JSON.stringify(b.formData.extraFilters || null) &&
JSON.stringify(a.formData.extraFormData || null) ===
JSON.stringify(b.formData.extraFormData || null)
);
}