diff --git a/src/datasources/asset/AssetConfigEditor.tsx b/src/datasources/asset/AssetConfigEditor.tsx index 03c8105e..44cd566d 100644 --- a/src/datasources/asset/AssetConfigEditor.tsx +++ b/src/datasources/asset/AssetConfigEditor.tsx @@ -56,6 +56,14 @@ export const AssetConfigEditor: React.FC = ({ options, onOptionsChange }) + + + + + + ); diff --git a/src/datasources/asset/AssetDataSource.test.ts b/src/datasources/asset/AssetDataSource.test.ts index 51c0bea2..523f27f9 100644 --- a/src/datasources/asset/AssetDataSource.test.ts +++ b/src/datasources/asset/AssetDataSource.test.ts @@ -12,6 +12,7 @@ import { AssetQueryType } from "./types/types"; import { AssetPresenceWithSystemConnectionModel, AssetsResponse } from "datasources/asset-common/types"; import { ListAssetsQuery } from "./types/ListAssets.types"; import { AssetVariableQuery } from "./types/AssetVariableQuery.types"; +import { QueryBuilderType } from "./constants/constants"; let ds: AssetDataSource, backendSrv: MockProxy let assetOptions = { @@ -306,10 +307,10 @@ const assetsWithoutNameResponseMock: AssetsResponse = "totalCount": 2 } - const assetMetadataQueryMock: ListAssetsQuery = { type: AssetQueryType.ListAssets, filter: 'Location.MinionId == "123"', + queryBuilderType: QueryBuilderType.Simple, refId: '' } @@ -362,6 +363,7 @@ describe('queries', () => { const query: AssetVariableQuery = { filter: '', type: AssetQueryType.None, + queryBuilderType: QueryBuilderType.Simple, refId: "" } @@ -382,6 +384,7 @@ describe('queries', () => { const query: AssetVariableQuery = { filter: '', type: AssetQueryType.None, + queryBuilderType: QueryBuilderType.Simple, refId: "" } diff --git a/src/datasources/asset/AssetDataSource.ts b/src/datasources/asset/AssetDataSource.ts index 055d6c10..3fc1b059 100644 --- a/src/datasources/asset/AssetDataSource.ts +++ b/src/datasources/asset/AssetDataSource.ts @@ -20,7 +20,7 @@ import { ListAssetsQuery } from './types/ListAssets.types'; import { ListAssetsDataSource } from './data-sources/list-assets/ListAssetsDataSource'; import { AssetSummaryDataSource } from './data-sources/asset-summary/AssetSummaryDataSource'; import { AssetModel } from 'datasources/asset-common/types'; -import { QUERY_LIMIT } from './constants/constants'; +import { QUERY_LIMIT, QueryBuilderType } from './constants/constants'; import { transformComputedFieldsQuery } from 'core/query-builder.utils'; import { AssetVariableQuery } from './types/AssetVariableQuery.types'; @@ -44,6 +44,7 @@ export class AssetDataSource extends DataSourceBase { diff --git a/src/datasources/asset/components/AssetQueryEditor.tsx b/src/datasources/asset/components/AssetQueryEditor.tsx index 3f9b48b2..8af04b54 100644 --- a/src/datasources/asset/components/AssetQueryEditor.tsx +++ b/src/datasources/asset/components/AssetQueryEditor.tsx @@ -21,6 +21,7 @@ export function AssetQueryEditor({ query, onChange, onRunQuery, datasource }: Pr assetList: datasource.instanceSettings.jsonData?.featureToggles?.assetList ?? AssetFeatureTogglesDefaults.assetList, calibrationForecast: datasource.instanceSettings.jsonData?.featureToggles?.calibrationForecast ?? AssetFeatureTogglesDefaults.calibrationForecast, assetSummary: datasource.instanceSettings.jsonData?.featureToggles?.assetSummary ?? AssetFeatureTogglesDefaults.assetSummary, + advancedFilter: datasource.instanceSettings.jsonData?.featureToggles?.advancedFilter ?? AssetFeatureTogglesDefaults.advancedFilter, }); const handleQueryChange = useCallback((value: AssetQuery, runQuery = false): void => { @@ -78,6 +79,7 @@ export function AssetQueryEditor({ query, onChange, onRunQuery, datasource }: Pr query={query as ListAssetsQuery} handleQueryChange={handleQueryChange} datasource={datasource.getListAssetsSource()} + complexFilterEnabled={assetFeatures.current.advancedFilter} /> )} {((assetFeatures.current.calibrationForecast && queryType === AssetQueryType.CalibrationForecast) || (query.type === AssetQueryType.CalibrationForecast)) && ( diff --git a/src/datasources/asset/components/editors/asset-summary/AssetSummaryEditor.test.tsx b/src/datasources/asset/components/editors/asset-summary/AssetSummaryEditor.test.tsx index 5471528d..d5e575b1 100644 --- a/src/datasources/asset/components/editors/asset-summary/AssetSummaryEditor.test.tsx +++ b/src/datasources/asset/components/editors/asset-summary/AssetSummaryEditor.test.tsx @@ -6,6 +6,7 @@ import { AssetSummaryResponse } from 'datasources/asset/types/AssetSummaryQuery. import { AssetDataSourceOptions, AssetQuery, AssetQueryType } from 'datasources/asset/types/types'; import { AssetSummaryDataSource } from '../../../data-sources/asset-summary/AssetSummaryDataSource'; import { assetSummaryFields } from '../../../constants/AssetSummaryQuery.constants'; +import { QueryBuilderType } from 'datasources/asset/constants/constants'; describe('AssetSummaryDataSource', () => { let dataSource: AssetSummaryDataSource; @@ -25,7 +26,7 @@ describe('AssetSummaryDataSource', () => { }); it('should process metadata query correctly', async () => { - const query: AssetQuery = { refId: 'A', type: AssetQueryType.AssetSummary, }; + const query: AssetQuery = { refId: 'A', type: AssetQueryType.AssetSummary, queryBuilderType: QueryBuilderType.Simple, }; jest.spyOn(dataSource, 'getAssetSummary').mockResolvedValue(assetSummary); const result = await dataSource.processSummaryQuery(query); diff --git a/src/datasources/asset/components/editors/calibration-forecast/CalibrationForecastEditor.test.tsx b/src/datasources/asset/components/editors/calibration-forecast/CalibrationForecastEditor.test.tsx index 0f3ef2b4..7b2f3b5d 100644 --- a/src/datasources/asset/components/editors/calibration-forecast/CalibrationForecastEditor.test.tsx +++ b/src/datasources/asset/components/editors/calibration-forecast/CalibrationForecastEditor.test.tsx @@ -10,6 +10,7 @@ import { CalibrationForecastDataSource } from '../../../data-sources/calibration import { AssetQueryEditor } from '../../AssetQueryEditor'; import { AssetDataSource } from '../../../AssetDataSource'; import { AssetQueryType } from '../../../types/types'; +import { QueryBuilderType } from 'datasources/asset/constants/constants'; class FakeAssetCalibrationDataSource extends CalibrationForecastDataSource {} @@ -72,6 +73,7 @@ describe('CalibrationForecastEditor', () => { refId: '', groupBy: [], type: AssetQueryType.CalibrationForecast, + queryBuilderType: QueryBuilderType.Simple, } as CalibrationForecastQuery); // User selects group by location diff --git a/src/datasources/asset/components/editors/calibration-forecast/query-builder/CalibrationForecastQueryBuilder.tsx b/src/datasources/asset/components/editors/calibration-forecast/query-builder/CalibrationForecastQueryBuilder.tsx index 2564d440..a8d62ea2 100644 --- a/src/datasources/asset/components/editors/calibration-forecast/query-builder/CalibrationForecastQueryBuilder.tsx +++ b/src/datasources/asset/components/editors/calibration-forecast/query-builder/CalibrationForecastQueryBuilder.tsx @@ -119,6 +119,7 @@ export const CalibrationForecastQueryBuilder: React.FC ); }; diff --git a/src/datasources/asset/components/editors/list-assets/ListAssetsEditor.test.tsx b/src/datasources/asset/components/editors/list-assets/ListAssetsEditor.test.tsx index eea1d489..59b5916b 100644 --- a/src/datasources/asset/components/editors/list-assets/ListAssetsEditor.test.tsx +++ b/src/datasources/asset/components/editors/list-assets/ListAssetsEditor.test.tsx @@ -6,6 +6,7 @@ import { setupRenderer } from '../../../../../test/fixtures'; import { ListAssetsQuery } from '../../../types/ListAssets.types'; import { AssetFeatureTogglesDefaults } from 'datasources/asset/types/types'; import { ListAssetsDataSource } from '../../../data-sources/list-assets/ListAssetsDataSource'; +import { QueryBuilderType } from 'datasources/asset/constants/constants'; const fakeSystems: SystemMetadata[] = [ { @@ -52,7 +53,7 @@ it('does not render when feature is not enabled', async () => { it('renders the query builder', async () => { assetDatasourceOptions.featureToggles.assetList = true; - render({} as ListAssetsQuery); + render({queryBuilderType: QueryBuilderType.Simple} as ListAssetsQuery); await waitFor(() => expect(screen.getAllByText('Property').length).toBe(1)); await waitFor(() => expect(screen.getAllByText('Operator').length).toBe(1)); diff --git a/src/datasources/asset/components/editors/list-assets/ListAssetsEditor.tsx b/src/datasources/asset/components/editors/list-assets/ListAssetsEditor.tsx index 909f82b0..1991ed07 100644 --- a/src/datasources/asset/components/editors/list-assets/ListAssetsEditor.tsx +++ b/src/datasources/asset/components/editors/list-assets/ListAssetsEditor.tsx @@ -14,9 +14,10 @@ type Props = { query: ListAssetsQuery; handleQueryChange: (value: AssetQuery, runQuery: boolean) => void; datasource: ListAssetsDataSource; + complexFilterEnabled: boolean; }; -export function ListAssetsEditor({ query, handleQueryChange, datasource }: Props) { +export function ListAssetsEditor({ query, handleQueryChange, datasource, complexFilterEnabled }: Props) { const [workspaces, setWorkspaces] = useState([]); const [systems, setSystems] = useState([]); const [areDependenciesLoaded, setAreDependenciesLoaded] = useState(false); @@ -50,6 +51,9 @@ export function ListAssetsEditor({ query, handleQueryChange, datasource }: Props globalVariableOptions={datasource.globalVariableOptions()} areDependenciesLoaded={areDependenciesLoaded} onChange={(event: any) => onParameterChange(event)} + query={query} + handleQueryChange={handleQueryChange} + complexFilterEnabled={complexFilterEnabled} > diff --git a/src/datasources/asset/components/editors/list-assets/query-builder/AssetQueryBuilder.test.tsx b/src/datasources/asset/components/editors/list-assets/query-builder/AssetQueryBuilder.test.tsx index 12cec816..b8ec16f9 100644 --- a/src/datasources/asset/components/editors/list-assets/query-builder/AssetQueryBuilder.test.tsx +++ b/src/datasources/asset/components/editors/list-assets/query-builder/AssetQueryBuilder.test.tsx @@ -3,6 +3,7 @@ import { AssetQueryBuilder } from './AssetQueryBuilder'; import { render } from '@testing-library/react'; import { QueryBuilderOption, Workspace } from 'core/types'; import { SystemMetadata } from 'datasources/system/types'; +import { QueryBuilderType } from 'datasources/asset/constants/constants'; describe('AssetQueryBuilder', () => { describe('useEffects', () => { @@ -10,7 +11,7 @@ describe('AssetQueryBuilder', () => { const containerClass = 'smart-filter-group-condition-container'; - function renderElement(workspaces: Workspace[], systems: SystemMetadata[], filter?: string, globalVariableOptions: QueryBuilderOption[] = []) { + function renderElement(workspaces: Workspace[], systems: SystemMetadata[], filter?: string, globalVariableOptions: QueryBuilderOption[] = [], complexFilterEnabled = true, queryBuilderType = QueryBuilderType.Simple) { reactNode = React.createElement(AssetQueryBuilder, { workspaces, systems, @@ -18,6 +19,9 @@ describe('AssetQueryBuilder', () => { globalVariableOptions, onChange: jest.fn(), areDependenciesLoaded: true, + query: {queryBuilderType: queryBuilderType} as any, + handleQueryChange: jest.fn(), + complexFilterEnabled: complexFilterEnabled, }); const renderResult = render(reactNode); return { @@ -30,6 +34,14 @@ describe('AssetQueryBuilder', () => { const { renderResult, conditionsContainer } = renderElement([], [], ''); expect(conditionsContainer.length).toBe(1); expect(renderResult.findByLabelText('Empty condition row')).toBeTruthy(); + expect(renderResult.queryByText('Advanced')).not.toBeNull(); + }); + + it('should not render complex option if complexFilterEnabled is disabled', () => { + const { renderResult, conditionsContainer } = renderElement([], [], '', [], false); + expect(conditionsContainer.length).toBe(1); + expect(renderResult.findByLabelText('Empty condition row')).toBeTruthy(); + expect(renderResult.queryByText('Advanced')).toBeNull(); }); it('should select workspace in query builder', () => { @@ -82,5 +94,14 @@ describe('AssetQueryBuilder', () => { expect(conditionsContainer?.length).toBe(1); expect(conditionsContainer.item(0)?.innerHTML).not.toContain('alert(\'Workspace\')'); }) + + it('should render the filter when advanced option is selected', () => { + const workspace = { id: '1', name: 'Selected workspace' } as Workspace; + const system = { id: '1', alias: 'Selected system' } as SystemMetadata; + + const { renderResult } = renderElement([workspace], [system], 'Location = "1"', [], true, QueryBuilderType.Advanced); + + expect(renderResult.queryByText('Location = "1"')).not.toBeNull(); + }) }); }); diff --git a/src/datasources/asset/components/editors/list-assets/query-builder/AssetQueryBuilder.tsx b/src/datasources/asset/components/editors/list-assets/query-builder/AssetQueryBuilder.tsx index 00cc811a..030bda45 100644 --- a/src/datasources/asset/components/editors/list-assets/query-builder/AssetQueryBuilder.tsx +++ b/src/datasources/asset/components/editors/list-assets/query-builder/AssetQueryBuilder.tsx @@ -1,6 +1,6 @@ -import React, { useEffect, useMemo, useState } from 'react'; +import React, { ChangeEvent, useCallback, useEffect, useMemo, useState } from 'react'; import { QueryBuilder, QueryBuilderCustomOperation, QueryBuilderProps } from 'smart-webcomponents-react/querybuilder'; -import { useTheme2 } from '@grafana/ui'; +import { RadioButtonGroup, TextArea, useTheme2 } from '@grafana/ui'; import 'smart-webcomponents-react/source/styles/smart.dark-orange.css'; import 'smart-webcomponents-react/source/styles/smart.orange.css'; @@ -14,7 +14,9 @@ import { expressionBuilderCallback, expressionReaderCallback } from 'core/query- import { SystemMetadata } from 'datasources/system/types'; import { QBField } from '../../../../types/CalibrationForecastQuery.types'; import { ListAssetsFields, ListAssetsStaticFields } from '../../../../constants/ListAssets.constants'; -import { filterXSSField, filterXSSLINQExpression } from 'core/utils'; +import { enumToOptions, filterXSSField, filterXSSLINQExpression } from 'core/utils'; +import { QueryBuilderType } from 'datasources/asset/constants/constants'; +import { ListAssetsQuery } from 'datasources/asset/types/ListAssets.types'; type AssetCalibrationQueryBuilderProps = QueryBuilderProps & React.HTMLAttributes & { @@ -23,6 +25,9 @@ type AssetCalibrationQueryBuilderProps = QueryBuilderProps & systems: SystemMetadata[]; globalVariableOptions: QueryBuilderOption[]; areDependenciesLoaded: boolean; + query: ListAssetsQuery; + handleQueryChange: ( value: ListAssetsQuery, runQuery: boolean ) => void; + complexFilterEnabled: boolean; }; export const AssetQueryBuilder: React.FC = ({ @@ -32,6 +37,9 @@ export const AssetQueryBuilder: React.FC = ({ systems, globalVariableOptions, areDependenciesLoaded, + query, + handleQueryChange, + complexFilterEnabled }) => { const theme = useTheme2(); document.body.setAttribute('theme', theme.isDark ? 'dark-orange' : 'orange'); @@ -43,6 +51,16 @@ export const AssetQueryBuilder: React.FC = ({ return filterXSSLINQExpression(filter); }, [filter]) + const onFilterChange = useCallback((event: ChangeEvent) => + { + handleQueryChange({...query, filter: event.target.value}, false); + }, [query, handleQueryChange]) + + const handleQueryBuilderTypeChange = useCallback((newQueryBuilderType: QueryBuilderType) => + { + handleQueryChange({...query, queryBuilderType: newQueryBuilderType}, false); + }, [query, handleQueryChange]) + const workspaceField = useMemo(() => { const workspaceField = ListAssetsFields.WORKSPACE; @@ -154,12 +172,29 @@ export const AssetQueryBuilder: React.FC = ({ }, [workspaceField, locationField, calibrationDueDateField, areDependenciesLoaded, globalVariableOptions]); return ( - + <> + { complexFilterEnabled && +
+ +
+ } + { (!complexFilterEnabled || query.queryBuilderType === QueryBuilderType.Simple) && + + } + { complexFilterEnabled && query.queryBuilderType === QueryBuilderType.Advanced && +