fix(explore): Dashboard filters on adhoc cols not working in Explore (#23239)

This commit is contained in:
Kamil Gabryjelski 2023-03-01 15:45:00 +01:00 committed by GitHub
parent 5a099e0762
commit 6076457b90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 221 additions and 121 deletions

View File

@ -70,6 +70,7 @@ import Control from './Control';
import { ExploreAlert } from './ExploreAlert'; import { ExploreAlert } from './ExploreAlert';
import { RunQueryButton } from './RunQueryButton'; import { RunQueryButton } from './RunQueryButton';
import { Operators } from '../constants'; import { Operators } from '../constants';
import { CLAUSES } from './controls/FilterControl/types';
const { confirm } = Modal; const { confirm } = Modal;
@ -317,7 +318,7 @@ export const ControlPanelsContainer = (props: ControlPanelsContainerProps) => {
setControlValue('adhoc_filters', [ setControlValue('adhoc_filters', [
...(adhoc_filters || []), ...(adhoc_filters || []),
{ {
clause: 'WHERE', clause: CLAUSES.WHERE,
subject: x_axis, subject: x_axis,
operator: Operators.TEMPORAL_RANGE, operator: Operators.TEMPORAL_RANGE,
comparator: defaultTimeFilter || NO_TIME_RANGE, comparator: defaultTimeFilter || NO_TIME_RANGE,

View File

@ -32,14 +32,13 @@ import { TimeseriesDefaultFormData } from '@superset-ui/plugin-chart-echarts';
import { render, screen } from 'spec/helpers/testing-library'; import { render, screen } from 'spec/helpers/testing-library';
import AdhocMetric from 'src/explore/components/controls/MetricControl/AdhocMetric'; import AdhocMetric from 'src/explore/components/controls/MetricControl/AdhocMetric';
import AdhocFilter, { import AdhocFilter from 'src/explore/components/controls/FilterControl/AdhocFilter';
EXPRESSION_TYPES,
} from 'src/explore/components/controls/FilterControl/AdhocFilter';
import { import {
DndFilterSelect, DndFilterSelect,
DndFilterSelectProps, DndFilterSelectProps,
} from 'src/explore/components/controls/DndColumnSelectControl/DndFilterSelect'; } from 'src/explore/components/controls/DndColumnSelectControl/DndFilterSelect';
import { PLACEHOLDER_DATASOURCE } from 'src/dashboard/constants'; import { PLACEHOLDER_DATASOURCE } from 'src/dashboard/constants';
import { EXPRESSION_TYPES } from '../FilterControl/types';
const defaultProps: DndFilterSelectProps = { const defaultProps: DndFilterSelectProps = {
type: 'DndFilterSelect', type: 'DndFilterSelect',

View File

@ -43,10 +43,7 @@ import { Datasource, OptionSortType } from 'src/explore/types';
import { OptionValueType } from 'src/explore/components/controls/DndColumnSelectControl/types'; import { OptionValueType } from 'src/explore/components/controls/DndColumnSelectControl/types';
import AdhocFilterPopoverTrigger from 'src/explore/components/controls/FilterControl/AdhocFilterPopoverTrigger'; import AdhocFilterPopoverTrigger from 'src/explore/components/controls/FilterControl/AdhocFilterPopoverTrigger';
import DndSelectLabel from 'src/explore/components/controls/DndColumnSelectControl/DndSelectLabel'; import DndSelectLabel from 'src/explore/components/controls/DndColumnSelectControl/DndSelectLabel';
import AdhocFilter, { import AdhocFilter from 'src/explore/components/controls/FilterControl/AdhocFilter';
CLAUSES,
EXPRESSION_TYPES,
} from 'src/explore/components/controls/FilterControl/AdhocFilter';
import AdhocMetric from 'src/explore/components/controls/MetricControl/AdhocMetric'; import AdhocMetric from 'src/explore/components/controls/MetricControl/AdhocMetric';
import { import {
DatasourcePanelDndItem, DatasourcePanelDndItem,
@ -58,6 +55,7 @@ import { ControlComponentProps } from 'src/explore/components/Control';
import AdhocFilterControl from '../FilterControl/AdhocFilterControl'; import AdhocFilterControl from '../FilterControl/AdhocFilterControl';
import DndAdhocFilterOption from './DndAdhocFilterOption'; import DndAdhocFilterOption from './DndAdhocFilterOption';
import { useDefaultTimeFilter } from '../DateFilterControl/utils'; import { useDefaultTimeFilter } from '../DateFilterControl/utils';
import { CLAUSES, EXPRESSION_TYPES } from '../FilterControl/types';
const { warning } = Modal; const { warning } = Modal;

View File

@ -16,11 +16,9 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
import AdhocFilter, { import AdhocFilter from 'src/explore/components/controls/FilterControl/AdhocFilter';
EXPRESSION_TYPES,
CLAUSES,
} from 'src/explore/components/controls/FilterControl/AdhocFilter';
import { Operators } from 'src/explore/constants'; import { Operators } from 'src/explore/constants';
import { EXPRESSION_TYPES, CLAUSES } from '../types';
describe('AdhocFilter', () => { describe('AdhocFilter', () => {
it('sets filterOptionName in constructor', () => { it('sets filterOptionName in constructor', () => {

View File

@ -21,60 +21,13 @@ import {
Operators, Operators,
OPERATOR_ENUM_TO_OPERATOR_TYPE, OPERATOR_ENUM_TO_OPERATOR_TYPE,
} from 'src/explore/constants'; } from 'src/explore/constants';
import { getSimpleSQLExpression } from 'src/explore/exploreUtils'; import { translateToSql } from '../utils/translateToSQL';
import { CLAUSES, EXPRESSION_TYPES } from '../types';
export const EXPRESSION_TYPES = {
SIMPLE: 'SIMPLE',
SQL: 'SQL',
};
export const CLAUSES = {
HAVING: 'HAVING',
WHERE: 'WHERE',
};
const OPERATORS_TO_SQL = {
'==': '=',
'!=': '<>',
'>': '>',
'<': '<',
'>=': '>=',
'<=': '<=',
IN: 'IN',
'NOT IN': 'NOT IN',
LIKE: 'LIKE',
ILIKE: 'ILIKE',
REGEX: 'REGEX',
'IS NOT NULL': 'IS NOT NULL',
'IS NULL': 'IS NULL',
'IS TRUE': 'IS TRUE',
'IS FALSE': 'IS FALSE',
'LATEST PARTITION': ({ datasource }) =>
`= '{{ presto.latest_partition('${datasource.schema}.${datasource.datasource_name}') }}'`,
};
const CUSTOM_OPERATIONS = [...CUSTOM_OPERATORS].map( const CUSTOM_OPERATIONS = [...CUSTOM_OPERATORS].map(
op => OPERATOR_ENUM_TO_OPERATOR_TYPE[op].operation, op => OPERATOR_ENUM_TO_OPERATOR_TYPE[op].operation,
); );
function translateToSql(adhocMetric, { useSimple } = {}) {
if (adhocMetric.expressionType === EXPRESSION_TYPES.SIMPLE || useSimple) {
const { subject, comparator } = adhocMetric;
const operator =
adhocMetric.operator &&
// 'LATEST PARTITION' supported callback only
adhocMetric.operator ===
OPERATOR_ENUM_TO_OPERATOR_TYPE[Operators.LATEST_PARTITION].operation
? OPERATORS_TO_SQL[adhocMetric.operator](adhocMetric)
: OPERATORS_TO_SQL[adhocMetric.operator];
return getSimpleSQLExpression(subject, operator, comparator);
}
if (adhocMetric.expressionType === EXPRESSION_TYPES.SQL) {
return adhocMetric.sqlExpression;
}
return '';
}
export default class AdhocFilter { export default class AdhocFilter {
constructor(adhocFilter) { constructor(adhocFilter) {
this.expressionType = adhocFilter.expressionType || EXPRESSION_TYPES.SIMPLE; this.expressionType = adhocFilter.expressionType || EXPRESSION_TYPES.SIMPLE;

View File

@ -22,10 +22,7 @@ import sinon from 'sinon';
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import { supersetTheme } from '@superset-ui/core'; import { supersetTheme } from '@superset-ui/core';
import AdhocFilter, { import AdhocFilter from 'src/explore/components/controls/FilterControl/AdhocFilter';
EXPRESSION_TYPES,
CLAUSES,
} from 'src/explore/components/controls/FilterControl/AdhocFilter';
import { LabelsContainer } from 'src/explore/components/controls/OptionControls'; import { LabelsContainer } from 'src/explore/components/controls/OptionControls';
import { import {
AGGREGATES, AGGREGATES,
@ -34,6 +31,7 @@ import {
} from 'src/explore/constants'; } from 'src/explore/constants';
import AdhocMetric from 'src/explore/components/controls/MetricControl/AdhocMetric'; import AdhocMetric from 'src/explore/components/controls/MetricControl/AdhocMetric';
import AdhocFilterControl from '.'; import AdhocFilterControl from '.';
import { CLAUSES, EXPRESSION_TYPES } from '../types';
const simpleAdhocFilter = new AdhocFilter({ const simpleAdhocFilter = new AdhocFilter({
expressionType: EXPRESSION_TYPES.SIMPLE, expressionType: EXPRESSION_TYPES.SIMPLE,

View File

@ -45,12 +45,10 @@ import Icons from 'src/components/Icons';
import Modal from 'src/components/Modal'; import Modal from 'src/components/Modal';
import AdhocFilterPopoverTrigger from 'src/explore/components/controls/FilterControl/AdhocFilterPopoverTrigger'; import AdhocFilterPopoverTrigger from 'src/explore/components/controls/FilterControl/AdhocFilterPopoverTrigger';
import AdhocFilterOption from 'src/explore/components/controls/FilterControl/AdhocFilterOption'; import AdhocFilterOption from 'src/explore/components/controls/FilterControl/AdhocFilterOption';
import AdhocFilter, { import AdhocFilter from 'src/explore/components/controls/FilterControl/AdhocFilter';
CLAUSES,
EXPRESSION_TYPES,
} from 'src/explore/components/controls/FilterControl/AdhocFilter';
import adhocFilterType from 'src/explore/components/controls/FilterControl/adhocFilterType'; import adhocFilterType from 'src/explore/components/controls/FilterControl/adhocFilterType';
import columnType from 'src/explore/components/controls/FilterControl/columnType'; import columnType from 'src/explore/components/controls/FilterControl/columnType';
import { CLAUSES, EXPRESSION_TYPES } from '../types';
const { warning } = Modal; const { warning } = Modal;

View File

@ -24,15 +24,13 @@ import Button from 'src/components/Button';
import ErrorBoundary from 'src/components/ErrorBoundary'; import ErrorBoundary from 'src/components/ErrorBoundary';
import Tabs from 'src/components/Tabs'; import Tabs from 'src/components/Tabs';
import AdhocFilter, { import AdhocFilter from 'src/explore/components/controls/FilterControl/AdhocFilter';
EXPRESSION_TYPES,
CLAUSES,
} from 'src/explore/components/controls/FilterControl/AdhocFilter';
import { AGGREGATES } from 'src/explore/constants'; import { AGGREGATES } from 'src/explore/constants';
import AdhocFilterEditPopoverSimpleTabContent from 'src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent'; import AdhocFilterEditPopoverSimpleTabContent from 'src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent';
import AdhocFilterEditPopoverSqlTabContent from 'src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSqlTabContent'; import AdhocFilterEditPopoverSqlTabContent from 'src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSqlTabContent';
import AdhocMetric from 'src/explore/components/controls/MetricControl/AdhocMetric'; import AdhocMetric from 'src/explore/components/controls/MetricControl/AdhocMetric';
import AdhocFilterEditPopover from '.'; import AdhocFilterEditPopover from '.';
import { CLAUSES, EXPRESSION_TYPES } from '../types';
const simpleAdhocFilter = new AdhocFilter({ const simpleAdhocFilter = new AdhocFilter({
expressionType: EXPRESSION_TYPES.SIMPLE, expressionType: EXPRESSION_TYPES.SIMPLE,

View File

@ -24,9 +24,7 @@ import { styled, t } from '@superset-ui/core';
import ErrorBoundary from 'src/components/ErrorBoundary'; import ErrorBoundary from 'src/components/ErrorBoundary';
import Tabs from 'src/components/Tabs'; import Tabs from 'src/components/Tabs';
import adhocMetricType from 'src/explore/components/controls/MetricControl/adhocMetricType'; import adhocMetricType from 'src/explore/components/controls/MetricControl/adhocMetricType';
import AdhocFilter, { import AdhocFilter from 'src/explore/components/controls/FilterControl/AdhocFilter';
EXPRESSION_TYPES,
} from 'src/explore/components/controls/FilterControl/AdhocFilter';
import AdhocFilterEditPopoverSimpleTabContent from 'src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent'; import AdhocFilterEditPopoverSimpleTabContent from 'src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSimpleTabContent';
import AdhocFilterEditPopoverSqlTabContent from 'src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSqlTabContent'; import AdhocFilterEditPopoverSqlTabContent from 'src/explore/components/controls/FilterControl/AdhocFilterEditPopoverSqlTabContent';
import columnType from 'src/explore/components/controls/FilterControl/columnType'; import columnType from 'src/explore/components/controls/FilterControl/columnType';
@ -34,6 +32,7 @@ import {
POPOVER_INITIAL_HEIGHT, POPOVER_INITIAL_HEIGHT,
POPOVER_INITIAL_WIDTH, POPOVER_INITIAL_WIDTH,
} from 'src/explore/constants'; } from 'src/explore/constants';
import { EXPRESSION_TYPES } from '../types';
const propTypes = { const propTypes = {
adhocFilter: PropTypes.instanceOf(AdhocFilter).isRequired, adhocFilter: PropTypes.instanceOf(AdhocFilter).isRequired,

View File

@ -25,10 +25,7 @@ import thunk from 'redux-thunk';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import configureStore from 'redux-mock-store'; import configureStore from 'redux-mock-store';
import AdhocFilter, { import AdhocFilter from 'src/explore/components/controls/FilterControl/AdhocFilter';
EXPRESSION_TYPES,
CLAUSES,
} from 'src/explore/components/controls/FilterControl/AdhocFilter';
import { import {
AGGREGATES, AGGREGATES,
Operators, Operators,
@ -46,6 +43,7 @@ import AdhocFilterEditPopoverSimpleTabContent, {
useSimpleTabFilterProps, useSimpleTabFilterProps,
Props, Props,
} from '.'; } from '.';
import { CLAUSES, EXPRESSION_TYPES } from '../types';
const simpleAdhocFilter = new AdhocFilter({ const simpleAdhocFilter = new AdhocFilter({
expressionType: EXPRESSION_TYPES.SIMPLE, expressionType: EXPRESSION_TYPES.SIMPLE,

View File

@ -38,10 +38,7 @@ import {
OPERATOR_ENUM_TO_OPERATOR_TYPE, OPERATOR_ENUM_TO_OPERATOR_TYPE,
} from 'src/explore/constants'; } from 'src/explore/constants';
import FilterDefinitionOption from 'src/explore/components/controls/MetricControl/FilterDefinitionOption'; import FilterDefinitionOption from 'src/explore/components/controls/MetricControl/FilterDefinitionOption';
import AdhocFilter, { import AdhocFilter from 'src/explore/components/controls/FilterControl/AdhocFilter';
EXPRESSION_TYPES,
CLAUSES,
} from 'src/explore/components/controls/FilterControl/AdhocFilter';
import { Tooltip } from 'src/components/Tooltip'; import { Tooltip } from 'src/components/Tooltip';
import { Input } from 'src/components/Input'; import { Input } from 'src/components/Input';
import { optionLabel } from 'src/utils/common'; import { optionLabel } from 'src/utils/common';
@ -54,6 +51,7 @@ import {
import useAdvancedDataTypes from './useAdvancedDataTypes'; import useAdvancedDataTypes from './useAdvancedDataTypes';
import { useDatePickerInAdhocFilter } from '../utils'; import { useDatePickerInAdhocFilter } from '../utils';
import { useDefaultTimeFilter } from '../../DateFilterControl/utils'; import { useDefaultTimeFilter } from '../../DateFilterControl/utils';
import { CLAUSES, EXPRESSION_TYPES } from '../types';
const StyledInput = styled(Input)` const StyledInput = styled(Input)`
margin-bottom: ${({ theme }) => theme.gridUnit * 4}px; margin-bottom: ${({ theme }) => theme.gridUnit * 4}px;

View File

@ -21,11 +21,9 @@ import React from 'react';
import sinon from 'sinon'; import sinon from 'sinon';
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
import AdhocFilter, { import AdhocFilter from 'src/explore/components/controls/FilterControl/AdhocFilter';
EXPRESSION_TYPES,
CLAUSES,
} from 'src/explore/components/controls/FilterControl/AdhocFilter';
import AdhocFilterEditPopoverSqlTabContent from '.'; import AdhocFilterEditPopoverSqlTabContent from '.';
import { CLAUSES, EXPRESSION_TYPES } from '../types';
const sqlAdhocFilter = new AdhocFilter({ const sqlAdhocFilter = new AdhocFilter({
expressionType: EXPRESSION_TYPES.SQL, expressionType: EXPRESSION_TYPES.SQL,

View File

@ -25,10 +25,8 @@ import sqlKeywords from 'src/SqlLab/utils/sqlKeywords';
import adhocMetricType from 'src/explore/components/controls/MetricControl/adhocMetricType'; import adhocMetricType from 'src/explore/components/controls/MetricControl/adhocMetricType';
import columnType from 'src/explore/components/controls/FilterControl/columnType'; import columnType from 'src/explore/components/controls/FilterControl/columnType';
import AdhocFilter, { import AdhocFilter from 'src/explore/components/controls/FilterControl/AdhocFilter';
EXPRESSION_TYPES, import { CLAUSES, EXPRESSION_TYPES } from '../types';
CLAUSES,
} from 'src/explore/components/controls/FilterControl/AdhocFilter';
const propTypes = { const propTypes = {
adhocFilter: PropTypes.instanceOf(AdhocFilter).isRequired, adhocFilter: PropTypes.instanceOf(AdhocFilter).isRequired,

View File

@ -19,11 +19,9 @@
import React from 'react'; import React from 'react';
import { render, screen, waitFor } from 'spec/helpers/testing-library'; import { render, screen, waitFor } from 'spec/helpers/testing-library';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
import AdhocFilter, { import AdhocFilter from 'src/explore/components/controls/FilterControl/AdhocFilter';
EXPRESSION_TYPES,
CLAUSES,
} from 'src/explore/components/controls/FilterControl/AdhocFilter';
import AdhocFilterOption, { AdhocFilterOptionProps } from '.'; import AdhocFilterOption, { AdhocFilterOptionProps } from '.';
import { CLAUSES, EXPRESSION_TYPES } from '../types';
const simpleAdhocFilter = new AdhocFilter({ const simpleAdhocFilter = new AdhocFilter({
expressionType: EXPRESSION_TYPES.SIMPLE, expressionType: EXPRESSION_TYPES.SIMPLE,

View File

@ -19,11 +19,9 @@
import React from 'react'; import React from 'react';
import { render, screen } from 'spec/helpers/testing-library'; import { render, screen } from 'spec/helpers/testing-library';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
import AdhocFilter, { import AdhocFilter from 'src/explore/components/controls/FilterControl/AdhocFilter';
EXPRESSION_TYPES,
CLAUSES,
} from 'src/explore/components/controls/FilterControl/AdhocFilter';
import AdhocFilterPopoverTrigger from '.'; import AdhocFilterPopoverTrigger from '.';
import { CLAUSES, EXPRESSION_TYPES } from '../types';
const simpleAdhocFilter = new AdhocFilter({ const simpleAdhocFilter = new AdhocFilter({
expressionType: EXPRESSION_TYPES.SIMPLE, expressionType: EXPRESSION_TYPES.SIMPLE,

View File

@ -17,7 +17,7 @@
* under the License. * under the License.
*/ */
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { EXPRESSION_TYPES, CLAUSES } from './AdhocFilter'; import { CLAUSES, EXPRESSION_TYPES } from './types';
export default PropTypes.oneOfType([ export default PropTypes.oneOfType([
PropTypes.shape({ PropTypes.shape({

View File

@ -0,0 +1,28 @@
/**
* 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.
*/
export enum EXPRESSION_TYPES {
SIMPLE = 'SIMPLE',
SQL = 'SQL',
}
export enum CLAUSES {
HAVING = 'HAVING',
WHERE = 'WHERE',
}

View File

@ -0,0 +1,76 @@
/**
* 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 {
AdhocFilter,
isFreeFormAdhocFilter,
isSimpleAdhocFilter,
SimpleAdhocFilter,
} from '@superset-ui/core';
import {
OPERATOR_ENUM_TO_OPERATOR_TYPE,
Operators,
} from 'src/explore/constants';
import { getSimpleSQLExpression } from 'src/explore/exploreUtils';
export const OPERATORS_TO_SQL = {
'==': '=',
'!=': '<>',
'>': '>',
'<': '<',
'>=': '>=',
'<=': '<=',
IN: 'IN',
'NOT IN': 'NOT IN',
LIKE: 'LIKE',
ILIKE: 'ILIKE',
REGEX: 'REGEX',
'IS NOT NULL': 'IS NOT NULL',
'IS NULL': 'IS NULL',
'IS TRUE': 'IS TRUE',
'IS FALSE': 'IS FALSE',
'LATEST PARTITION': ({
datasource,
}: {
datasource: { schema: string; datasource_name: string };
}) =>
`= '{{ presto.latest_partition('${datasource.schema}.${datasource.datasource_name}') }}'`,
};
export const translateToSql = (
adhocFilter: AdhocFilter,
{ useSimple }: { useSimple: boolean } = { useSimple: false },
) => {
if (isSimpleAdhocFilter(adhocFilter) || useSimple) {
const { subject, operator } = adhocFilter as SimpleAdhocFilter;
const comparator =
'comparator' in adhocFilter ? adhocFilter.comparator : undefined;
const op =
operator &&
// 'LATEST PARTITION' supported callback only
operator ===
OPERATOR_ENUM_TO_OPERATOR_TYPE[Operators.LATEST_PARTITION].operation
? OPERATORS_TO_SQL[operator](adhocFilter)
: OPERATORS_TO_SQL[operator];
return getSimpleSQLExpression(subject, op, comparator);
}
if (isFreeFormAdhocFilter(adhocFilter)) {
return adhocFilter.sqlExpression;
}
return '';
};

View File

@ -21,7 +21,8 @@ import { NO_TIME_RANGE } from '@superset-ui/core';
import { Operators } from 'src/explore/constants'; import { Operators } from 'src/explore/constants';
import * as FetchTimeRangeModule from 'src/explore/components/controls/DateFilterControl'; import * as FetchTimeRangeModule from 'src/explore/components/controls/DateFilterControl';
import { useGetTimeRangeLabel } from './useGetTimeRangeLabel'; import { useGetTimeRangeLabel } from './useGetTimeRangeLabel';
import AdhocFilter, { CLAUSES, EXPRESSION_TYPES } from '../AdhocFilter'; import AdhocFilter from '../AdhocFilter';
import { CLAUSES, EXPRESSION_TYPES } from '../types';
test('should return empty object if operator is not TEMPORAL_RANGE', () => { test('should return empty object if operator is not TEMPORAL_RANGE', () => {
const adhocFilter = new AdhocFilter({ const adhocFilter = new AdhocFilter({

View File

@ -20,7 +20,8 @@ import { useEffect, useState } from 'react';
import { NO_TIME_RANGE } from '@superset-ui/core'; import { NO_TIME_RANGE } from '@superset-ui/core';
import { fetchTimeRange } from 'src/explore/components/controls/DateFilterControl'; import { fetchTimeRange } from 'src/explore/components/controls/DateFilterControl';
import { Operators } from 'src/explore/constants'; import { Operators } from 'src/explore/constants';
import AdhocFilter, { EXPRESSION_TYPES } from '../AdhocFilter'; import AdhocFilter from '../AdhocFilter';
import { EXPRESSION_TYPES } from '../types';
interface Results { interface Results {
actualTimeRange?: string; actualTimeRange?: string;

View File

@ -129,6 +129,15 @@ const getDashboardFormData = (overrides: JsonObject = {}) => ({
op: '<=', op: '<=',
val: 10000, val: 10000,
}, },
{
col: {
sqlExpression: 'totally viable sql expression',
expressionType: 'SQL',
label: 'My column',
},
op: 'IN',
val: ['Value1', 'Value2'],
},
], ],
granularity_sqla: 'ds', granularity_sqla: 'ds',
time_range: 'Last month', time_range: 'Last month',
@ -179,6 +188,7 @@ const getExpectedResultFormData = (overrides: JsonObject = {}) => ({
clause: 'WHERE', clause: 'WHERE',
expressionType: 'SIMPLE', expressionType: 'SIMPLE',
operator: 'IN', operator: 'IN',
operatorId: 'IN',
subject: 'name', subject: 'name',
comparator: ['Aaron'], comparator: ['Aaron'],
isExtra: true, isExtra: true,
@ -188,11 +198,19 @@ const getExpectedResultFormData = (overrides: JsonObject = {}) => ({
clause: 'WHERE', clause: 'WHERE',
expressionType: 'SIMPLE', expressionType: 'SIMPLE',
operator: '<=', operator: '<=',
operatorId: 'LESS_THAN_OR_EQUAL',
subject: 'num_boys', subject: 'num_boys',
comparator: 10000, comparator: 10000,
isExtra: true, isExtra: true,
filterOptionName: expect.any(String), filterOptionName: expect.any(String),
}, },
{
clause: 'WHERE',
expressionType: 'SQL',
sqlExpression: `(totally viable sql expression) IN ('Value1', 'Value2')`,
filterOptionName: expect.any(String),
isExtra: true,
},
], ],
adhoc_filters_b: [ adhoc_filters_b: [
{ {
@ -208,6 +226,7 @@ const getExpectedResultFormData = (overrides: JsonObject = {}) => ({
clause: 'WHERE', clause: 'WHERE',
expressionType: 'SIMPLE', expressionType: 'SIMPLE',
operator: 'IN', operator: 'IN',
operatorId: 'IN',
subject: 'name', subject: 'name',
comparator: ['Aaron'], comparator: ['Aaron'],
isExtra: true, isExtra: true,
@ -217,11 +236,19 @@ const getExpectedResultFormData = (overrides: JsonObject = {}) => ({
clause: 'WHERE', clause: 'WHERE',
expressionType: 'SIMPLE', expressionType: 'SIMPLE',
operator: '<=', operator: '<=',
operatorId: 'LESS_THAN_OR_EQUAL',
subject: 'num_boys', subject: 'num_boys',
comparator: 10000, comparator: 10000,
isExtra: true, isExtra: true,
filterOptionName: expect.any(String), filterOptionName: expect.any(String),
}, },
{
clause: 'WHERE',
expressionType: 'SQL',
sqlExpression: `(totally viable sql expression) IN ('Value1', 'Value2')`,
filterOptionName: expect.any(String),
isExtra: true,
},
], ],
applied_time_extras: { applied_time_extras: {
__time_grain: 'P1D', __time_grain: 'P1D',
@ -282,6 +309,15 @@ const getExpectedResultFormData = (overrides: JsonObject = {}) => ({
op: '<=', op: '<=',
val: 10000, val: 10000,
}, },
{
col: {
expressionType: 'SQL',
label: 'My column',
sqlExpression: 'totally viable sql expression',
},
op: 'IN',
val: ['Value1', 'Value2'],
},
], ],
granularity_sqla: 'ds', granularity_sqla: 'ds',
time_range: 'Last month', time_range: 'Last month',

View File

@ -18,31 +18,55 @@
*/ */
import isEqual from 'lodash/isEqual'; import isEqual from 'lodash/isEqual';
import { import {
EXTRA_FORM_DATA_OVERRIDE_REGULAR_MAPPINGS,
EXTRA_FORM_DATA_OVERRIDE_EXTRA_KEYS,
isDefined,
JsonObject,
ensureIsArray,
QueryObjectFilterClause,
SimpleAdhocFilter,
QueryFormData,
AdhocFilter, AdhocFilter,
ensureIsArray,
EXTRA_FORM_DATA_OVERRIDE_EXTRA_KEYS,
EXTRA_FORM_DATA_OVERRIDE_REGULAR_MAPPINGS,
isAdhocColumn,
isDefined,
isFreeFormAdhocFilter, isFreeFormAdhocFilter,
isSimpleAdhocFilter, isSimpleAdhocFilter,
JsonObject,
NO_TIME_RANGE, NO_TIME_RANGE,
QueryFormData,
QueryObjectFilterClause,
SimpleAdhocFilter,
} from '@superset-ui/core'; } from '@superset-ui/core';
import { OPERATOR_ENUM_TO_OPERATOR_TYPE } from '../constants';
import { translateToSql } from '../components/controls/FilterControl/utils/translateToSQL';
import {
CLAUSES,
EXPRESSION_TYPES,
} from '../components/controls/FilterControl/types';
const simpleFilterToAdhoc = ( const simpleFilterToAdhoc = (
filterClause: QueryObjectFilterClause, filterClause: QueryObjectFilterClause,
clause = 'where', clause: CLAUSES = CLAUSES.WHERE,
) => { ) => {
const result = { let result: AdhocFilter;
clause: clause.toUpperCase(), if (isAdhocColumn(filterClause.col)) {
expressionType: 'SIMPLE', result = {
operator: filterClause.op, expressionType: 'SQL',
subject: filterClause.col, clause,
comparator: 'val' in filterClause ? filterClause.val : undefined, sqlExpression: translateToSql({
} as SimpleAdhocFilter; expressionType: EXPRESSION_TYPES.SIMPLE,
subject: `(${filterClause.col.sqlExpression})`,
operator: filterClause.op,
comparator: 'val' in filterClause ? filterClause.val : undefined,
} as SimpleAdhocFilter),
};
} else {
result = {
expressionType: 'SIMPLE',
clause,
operator: filterClause.op,
operatorId: Object.entries(OPERATOR_ENUM_TO_OPERATOR_TYPE).find(
operatorEntry => operatorEntry[1].operation === filterClause.op,
)?.[0],
subject: filterClause.col,
comparator: 'val' in filterClause ? filterClause.val : undefined,
} as SimpleAdhocFilter;
}
if (filterClause.isExtra) { if (filterClause.isExtra) {
Object.assign(result, { Object.assign(result, {
isExtra: true, isExtra: true,

View File

@ -25,6 +25,10 @@ import {
ExtraFormData, ExtraFormData,
} from '@superset-ui/core'; } from '@superset-ui/core';
import { FALSE_STRING, NULL_STRING, TRUE_STRING } from 'src/utils/common'; import { FALSE_STRING, NULL_STRING, TRUE_STRING } from 'src/utils/common';
import {
CLAUSES,
EXPRESSION_TYPES,
} from '../explore/components/controls/FilterControl/types';
export const getSelectExtraFormData = ( export const getSelectExtraFormData = (
col: string, col: string,
@ -36,8 +40,8 @@ export const getSelectExtraFormData = (
if (emptyFilter) { if (emptyFilter) {
extra.adhoc_filters = [ extra.adhoc_filters = [
{ {
expressionType: 'SQL', expressionType: EXPRESSION_TYPES.SQL,
clause: 'WHERE', clause: CLAUSES.WHERE,
sqlExpression: '1 = 0', sqlExpression: '1 = 0',
}, },
]; ];