Skip to content

Commit

Permalink
feat: native filter components (#840)
Browse files Browse the repository at this point in the history
* feat: add AntD filter components

* add missing files

* add filter hook

* fix hook name

* required thumbnail

* add native filters to where clause

* add isNativeFilter flag to ChartMetadata

* rename native_filters to extra_form_data

* lint

* move hook to SuperChart props

* add NOOP default to setSelectedValues

* replace extra_form_data with override_ and append_

* add support for appending freeform where

* address review comments

* Add default values

* Update packages/superset-ui-core/test/query/processExtraFormData.test.ts

Co-authored-by: Evan Rusackas <evan@preset.io>

* Update packages/superset-ui-core/test/query/processExtraFormData.test.ts

Co-authored-by: Evan Rusackas <evan@preset.io>

* add storybook entry and change width to 100%

* add placeholder

* add default thumbnails

Co-authored-by: Evan Rusackas <evan@preset.io>
  • Loading branch information
2 people authored and zhaoyongjie committed Nov 26, 2021
1 parent c527f55 commit a52cd69
Show file tree
Hide file tree
Showing 47 changed files with 2,173 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ should be written in Typescript.
| [@superset-ui/plugin-chart-table](https://github.com/apache-superset/superset-ui/tree/master/plugins/plugin-chart-table) | [![Version](https://img.shields.io/npm/v/@superset-ui/plugin-chart-table.svg?style=flat-square)](https://www.npmjs.com/package/@superset-ui/plugin-chart-table) | |
| [@superset-ui/preset-chart-xy](https://github.com/apache-superset/superset-ui/tree/master/plugins/preset-chart-xy) | [![Version](https://img.shields.io/npm/v/@superset-ui/preset-chart-xy.svg?style=flat-square)](https://www.npmjs.com/package/@superset-ui/preset-chart-xy) | |
| [@superset-ui/plugin-chart-echarts](https://github.com/apache-superset/superset-ui/tree/master/plugins/plugin-chart-echarts) | [![Version](https://img.shields.io/npm/v/@superset-ui/plugin-chart-echarts.svg?style=flat-square)](https://www.npmjs.com/package/@superset-ui/plugin-chart-echarts) | |
| [@superset-ui/plugin-filter-antd](https://github.com/apache-superset/superset-ui/tree/master/plugins/plugin-filter-antd) | [![Version](https://img.shields.io/npm/v/@superset-ui/plugin-chart-echarts.svg?style=flat-square)](https://www.npmjs.com/package/@superset-ui/plugin-filter-antd) | |

## Contribution and development guide

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ export { default as getChartTransformPropsRegistry } from './registries/ChartTra

export { default as ChartDataProvider } from './components/ChartDataProvider';

export { SetExtraFormDataHook } from './types/Base';
export * from './types/TransformFunction';
export * from './types/QueryResponse';
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface ChartMetadataConfig {
supportedAnnotationTypes?: string[];
thumbnail: string;
useLegacyApi?: boolean;
isNativeFilter?: boolean;
}

export default class ChartMetadata {
Expand All @@ -32,6 +33,8 @@ export default class ChartMetadata {

useLegacyApi: boolean;

isNativeFilter: boolean;

constructor(config: ChartMetadataConfig) {
const {
name,
Expand All @@ -42,6 +45,7 @@ export default class ChartMetadata {
supportedAnnotationTypes = [],
thumbnail,
useLegacyApi = false,
isNativeFilter = false,
} = config;

this.name = name;
Expand All @@ -61,6 +65,7 @@ export default class ChartMetadata {
this.supportedAnnotationTypes = supportedAnnotationTypes;
this.thumbnail = thumbnail;
this.useLegacyApi = useLegacyApi;
this.isNativeFilter = isNativeFilter;
}

canBeAnnotationType(type: string): boolean {
Expand All @@ -77,6 +82,7 @@ export default class ChartMetadata {
supportedAnnotationTypes: this.supportedAnnotationTypes,
thumbnail: this.thumbnail,
useLegacyApi: this.useLegacyApi,
isNativeFilter: this.isNativeFilter,
});
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createSelector } from 'reselect';
import { convertKeysToCamelCase, Datasource } from '../..';
import { HandlerFunction, PlainObject } from '../types/Base';
import { QueryData, DataRecordFilters } from '../types/QueryResponse';
import { HandlerFunction, PlainObject, SetExtraFormDataHook } from '../types/Base';
import { QueryData, DataRecordFilters } from '..';

// TODO: more specific typing for these fields of ChartProps
type AnnotationData = PlainObject;
Expand All @@ -23,6 +23,8 @@ type Hooks = {
onError?: HandlerFunction;
/** use the vis as control to update state */
setControlValue?: HandlerFunction;
/** handle native filters */
setExtraFormData?: SetExtraFormDataHook;
/** handle tooltip */
setTooltip?: HandlerFunction;
} & PlainObject;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { ExtraFormData } from '../../query';

export type HandlerFunction = (...args: unknown[]) => void;

export type SetExtraFormDataHook = {
(extraFormData: ExtraFormData): void;
};

export interface PlainObject {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[key: string]: any;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import convertMetric from './convertMetric';
import processFilters from './processFilters';
import extractExtras from './extractExtras';
import extractQueryFields from './extractQueryFields';
import { appendExtraFormData, overrideExtraFormData } from './processExtraFormData';

export const DTTM_ALIAS = '__timestamp';

Expand All @@ -19,6 +20,7 @@ export const DTTM_ALIAS = '__timestamp';
export default function buildQueryObject<T extends QueryFormData>(formData: T): QueryObject {
const {
annotation_layers = [],
extra_form_data = {},
time_range,
since,
until,
Expand All @@ -32,6 +34,7 @@ export default function buildQueryObject<T extends QueryFormData>(formData: T):
url_params = {},
...residualFormData
} = formData;
const { append_form_data = {}, override_form_data = {} } = extra_form_data;

const numericRowLimit = Number(row_limit);
const numericRowOffset = Number(row_offset);
Expand All @@ -44,7 +47,7 @@ export default function buildQueryObject<T extends QueryFormData>(formData: T):
...extras,
});

return {
let queryObject: QueryObject = {
time_range,
since,
until,
Expand All @@ -65,4 +68,8 @@ export default function buildQueryObject<T extends QueryFormData>(formData: T):
: null,
url_params,
};
// append and override extra form data used by native filters
queryObject = appendExtraFormData(queryObject, append_form_data);
queryObject = overrideExtraFormData(queryObject, override_form_data);
return queryObject;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* 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 { QueryObject } from './types/Query';

const ALLOWED_OVERRIDES = ['time_grain_sqla', 'time_range', 'since', 'until'];
const ALLOWED_APPENDS = ['adhoc_filters', 'filters', 'groupby'];

export function overrideExtraFormData(
queryObject: QueryObject,
overrideFormData: Partial<QueryObject>,
): QueryObject {
const overriddenFormData = { ...queryObject };
ALLOWED_OVERRIDES.forEach(key => {
if (key in overrideFormData) {
overriddenFormData[key] = overrideFormData[key];
}
});
return overriddenFormData;
}

export function appendExtraFormData(
queryObject: QueryObject,
appendFormData: Partial<QueryObject>,
): QueryObject {
const appendedFormData = { ...queryObject };
ALLOWED_APPENDS.forEach(key => {
if (key in appendFormData) {
const append = appendFormData[key];
const currentValue = appendedFormData[key] || [];
// @ts-ignore
currentValue.push(...append);
appendedFormData[key] = currentValue;
}
});

// Add freeform where
const { extras = {} } = appendedFormData;
const { where = '' } = extras;
extras.where = where;

const { extras: appendExtras = {} } = appendFormData;
let { where: appendWhere } = appendExtras;

if (appendWhere) {
appendedFormData.extras = extras;
appendWhere = `(${appendWhere})`;
}
if (where) {
appendWhere = `(${where}) AND ${appendWhere}`;
}
extras.where = appendWhere;

return appendedFormData;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,53 +5,46 @@ import { isSimpleAdhocFilter } from './types/Filter';
import convertFilter from './convertFilter';

/** Logic formerly in viz.py's process_query_filters */
export default function processFilters(formData: QueryFormData) {
export default function processFilters(formData: QueryFormData): Partial<QueryFormData> {
// Split adhoc_filters into four fields according to
// (1) clause (WHERE or HAVING)
// (2) expressionType
// 2.1 SIMPLE (subject + operator + comparator)
// 2.2 SQL (freeform SQL expression))
const { adhoc_filters } = formData;
if (Array.isArray(adhoc_filters)) {
const simpleWhere: QueryObjectFilterClause[] = formData.filters || [];
const simpleHaving: QueryObjectFilterClause[] = [];
const freeformWhere: string[] = [];
if (formData.where) freeformWhere.push(formData.where);
const freeformHaving: string[] = [];
const { adhoc_filters = [], extras = {}, filters = [], where } = formData;
const simpleWhere: QueryObjectFilterClause[] = filters;

adhoc_filters.forEach(filter => {
const { clause } = filter;
if (isSimpleAdhocFilter(filter)) {
const filterClause = convertFilter(filter);
if (clause === 'WHERE') {
simpleWhere.push(filterClause);
} else {
simpleHaving.push(filterClause);
}
const simpleHaving: QueryObjectFilterClause[] = [];
const freeformWhere: string[] = [];
if (where) freeformWhere.push(where);
const freeformHaving: string[] = [];

adhoc_filters.forEach(filter => {
const { clause } = filter;
if (isSimpleAdhocFilter(filter)) {
const filterClause = convertFilter(filter);
if (clause === 'WHERE') {
simpleWhere.push(filterClause);
} else {
const { sqlExpression } = filter;
if (clause === 'WHERE') {
freeformWhere.push(sqlExpression);
} else {
freeformHaving.push(sqlExpression);
}
simpleHaving.push(filterClause);
}
});

// some filter-related fields need to go in `extras`
const extras = {
having: freeformHaving.map(exp => `(${exp})`).join(' AND '),
having_druid: simpleHaving,
where: freeformWhere.map(exp => `(${exp})`).join(' AND '),
...formData.extras,
};
} else {
const { sqlExpression } = filter;
if (clause === 'WHERE') {
freeformWhere.push(sqlExpression);
} else {
freeformHaving.push(sqlExpression);
}
}
});

return {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
filters: simpleWhere,
extras,
};
}
// some filter-related fields need to go in `extras`
extras.having = freeformHaving.map(exp => `(${exp})`).join(' AND ');
extras.having_druid = simpleHaving;
extras.where = freeformWhere.map(exp => `(${exp})`).join(' AND ');

return {};
return {
filters: simpleWhere,
extras,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ export type QueryObjectFilterClause = {
} & (
| {
op: BinaryOperator;
val: string;
val: string | number | boolean;
}
| {
op: SetOperator;
val: string[];
val: (string | number | boolean)[];
}
| {
op: UnaryOperator;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { TimeRange } from './Time';
import { AdhocFilter } from './Filter';
import { BinaryOperator, SetOperator } from './Operator';
import { AnnotationLayer } from './AnnotationLayer';
import { QueryObject } from './Query';

export type QueryFormDataMetric = string | AdhocMetric;
export type QueryFormResidualDataValue = string | AdhocMetric;
Expand Down Expand Up @@ -33,6 +34,11 @@ export type QueryFormExtraFilter = {
}
);

export type ExtraFormData = {
append_form_data?: Partial<QueryObject>;
override_form_data?: Partial<QueryObject>;
};

// Type signature for formData shared by all viz types
// It will be gradually filled out as we build out the query object

Expand Down Expand Up @@ -76,6 +82,7 @@ export type BaseFormData = {
annotation_layers?: AnnotationLayer[];
url_params?: Record<string, string>;
} & TimeRange &
ExtraFormData &
QueryFormResidualData;

// FormData is either sqla-based or druid-based
Expand Down
Original file line number Diff line number Diff line change
@@ -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 { DatasourceKey } from '@superset-ui/core/src/query';

describe('DatasourceKey', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -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 { buildQueryContext } from '@superset-ui/core/src/query';

describe('buildQueryContext', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -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 { AnnotationLayer, buildQueryObject, QueryObject } from '@superset-ui/core/src/query';

describe('buildQueryObject', () => {
Expand Down

0 comments on commit a52cd69

Please sign in to comment.