fix(#14326): tier dropdown is not working in advance search (#14780)

* improvement in advance search based on custom property

* fix a reading undefined property issue

* wip: advance search based on tier

* some code cleanup and improvement

* some fixes

* fix: ui flicker when advanceSearched is apply and refresh the page

* some cleanup

* no need to call customproperty api call, if entity not suppport customProperties

* minor change

* fix: autocomplete not working in tier search option in advance search modal

* added unit test for advance search provider component

* some cleanup

* added testcase for open modal

* added testcase for resetAllFilters method

* removed unwanted code

* added e2e test for testing tier advance search

* fix: e2e search flow for single field

* fix: string field not working after giving listValues in TierSearch

* fix: group query e2e test fix

* used asyncFetch way to get the tierOptions synchronously

* some cleanup

* remove unwanted lines

* some cleanup

* fix: selected option show option value instead of option title
This commit is contained in:
Abhishek Porwal 2024-01-23 17:45:38 +05:30 committed by GitHub
parent 740541c0c7
commit 9c92beaecd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 338 additions and 142 deletions

View File

@ -138,16 +138,15 @@ export const FIELDS = {
searchCriteriaSecondGroup: 'PersonalData.SpecialCategory',
responseValueSecondGroup: '"tagFQN":"PersonalData.SpecialCategory"',
},
// skipping tier for now, as it is not working, BE need to fix it
// Tiers: {
// name: 'Tier',
// testid: '[title="Tier"]',
// searchCriteriaFirstGroup: 'Tier.Tier1',
// responseValueFirstGroup: '"tagFQN":"Tier.Tier1"',
// searchCriteriaSecondGroup: 'Tier.Tier2',
// responseValueSecondGroup: '"tagFQN":"Tier.Tier2"',
// },
Tiers: {
name: 'Tier',
testid: '[title="Tier"]',
searchCriteriaFirstGroup: 'Tier.Tier1',
responseValueFirstGroup: '"tagFQN":"Tier.Tier1"',
searchCriteriaSecondGroup: 'Tier.Tier2',
responseValueSecondGroup: '"tagFQN":"Tier.Tier2"',
isLocalSearch: true,
},
Service: {
name: 'Service',
testid: '[title="Service"]',
@ -193,12 +192,21 @@ export const OPERATOR = {
},
};
export const searchForField = (condition, fieldid, searchCriteria, index) => {
export const searchForField = (
condition,
fieldId,
searchCriteria,
index,
isLocalSearch = false
) => {
if (!isLocalSearch) {
interceptURL('GET', '/api/v1/search/aggregate?*', 'suggestApi');
}
// Click on field dropdown
cy.get('.rule--field > .ant-select > .ant-select-selector').eq(index).click();
// Select owner fields
cy.get(`${fieldid}`).eq(index).click();
cy.get(`${fieldId}`).eq(index).click();
// Select the condition
cy.get('.rule--operator > .ant-select > .ant-select-selector')
.eq(index)
@ -219,8 +227,16 @@ export const searchForField = (condition, fieldid, searchCriteria, index) => {
cy.get('.widget--widget > .ant-select > .ant-select-selector')
.eq(index)
.type(searchCriteria);
// checking filter is working
cy.get(
`.ant-select-item-option-active[title="${searchCriteria}"]`
).should('be.visible');
// select value from dropdown
if (!isLocalSearch) {
verifyResponseStatusCode('@suggestApi', 200);
}
cy.get(`.ant-select-dropdown [title = '${searchCriteria}']`)
.trigger('mouseover')
.trigger('click');
@ -240,12 +256,13 @@ export const checkmustPaths = (
field,
searchCriteria,
index,
responseSearch
responseSearch,
isLocalSearch
) => {
goToAdvanceSearch();
// Search with advance search
searchForField(condition, field, searchCriteria, index);
searchForField(condition, field, searchCriteria, index, isLocalSearch);
interceptURL(
'GET',
@ -271,12 +288,13 @@ export const checkmust_notPaths = (
field,
searchCriteria,
index,
responseSearch
responseSearch,
isLocalSearch
) => {
goToAdvanceSearch();
// Search with advance search
searchForField(condition, field, searchCriteria, index);
searchForField(condition, field, searchCriteria, index, isLocalSearch);
interceptURL(
'GET',
`/api/v1/search/query?q=&index=*&from=0&size=10&deleted=false&query_filter=*must_not*${encodeURI(
@ -417,16 +435,17 @@ export const addTag = ({ tag, term, serviceName, entity }) => {
export const checkAddGroupWithOperator = (
condition_1,
condition_2,
fieldid,
fieldId,
searchCriteria_1,
searchCriteria_2,
index_1,
index_2,
operatorindex,
operatorIndex,
filter_1,
filter_2,
response_1,
response_2
response_2,
isLocalSearch = false
) => {
goToAdvanceSearch();
// Click on field dropdown
@ -435,7 +454,7 @@ export const checkAddGroupWithOperator = (
.should('be.visible')
.click();
// Select owner fields
cy.get(fieldid).eq(0).should('be.visible').click();
cy.get(fieldId).eq(0).should('be.visible').click();
// Select the condition
cy.get('.rule--operator > .ant-select > .ant-select-selector')
.eq(index_1)
@ -456,13 +475,17 @@ export const checkAddGroupWithOperator = (
.should('be.visible')
.type(searchCriteria_1);
} else {
if (!isLocalSearch) {
interceptURL('GET', '/api/v1/search/aggregate?*', 'suggestApi');
}
cy.get('.widget--widget > .ant-select > .ant-select-selector')
.eq(index_1)
.should('be.visible')
.type(searchCriteria_1);
if (!isLocalSearch) {
verifyResponseStatusCode('@suggestApi', 200);
}
cy.get('.ant-select-dropdown')
.not('.ant-select-dropdown-hidden')
.find(`[title="${searchCriteria_1}"]`)
@ -484,13 +507,13 @@ export const checkAddGroupWithOperator = (
// Select the AND/OR condition
cy.get(
`.group--conjunctions > .ant-btn-group > :nth-child(${operatorindex})`
`.group--conjunctions > .ant-btn-group > :nth-child(${operatorIndex})`
).click();
// Click on field dropdown
cy.get('.rule--field').eq(index_2).should('be.visible').click();
cy.get(fieldid).eq(2).should('be.visible').click();
cy.get(fieldId).eq(2).should('be.visible').click();
// Select the condition
cy.get('.rule--operator').eq(index_2).should('be.visible').click();
@ -509,12 +532,17 @@ export const checkAddGroupWithOperator = (
.should('be.visible')
.type(searchCriteria_2);
} else {
if (!isLocalSearch) {
interceptURL('GET', '/api/v1/search/aggregate?*', 'suggestApi');
}
cy.get('.widget--widget > .ant-select > .ant-select-selector')
.eq(index_2)
.should('be.visible')
.type(searchCriteria_2);
if (!isLocalSearch) {
verifyResponseStatusCode('@suggestApi', 200);
}
cy.get('.ant-select-dropdown')
.not('.ant-select-dropdown-hidden')
@ -547,12 +575,12 @@ export const checkAddGroupWithOperator = (
export const checkAddRuleWithOperator = (
condition_1,
condition_2,
fieldid,
fieldId,
searchCriteria_1,
searchCriteria_2,
index_1,
index_2,
operatorindex,
operatorIndex,
filter_1,
filter_2,
response_1,
@ -562,7 +590,7 @@ export const checkAddRuleWithOperator = (
// Click on field dropdown
cy.get('.rule--field').eq(index_1).should('be.visible').click();
// Select owner fields
cy.get(fieldid).eq(0).should('be.visible').click();
cy.get(fieldId).eq(0).should('be.visible').click();
// Select the condition
cy.get('.rule--operator').eq(index_1).should('be.visible').click();
@ -604,13 +632,13 @@ export const checkAddRuleWithOperator = (
// Select the AND/OR condition
cy.get(
`.group--conjunctions > .ant-btn-group > :nth-child(${operatorindex})`
`.group--conjunctions > .ant-btn-group > :nth-child(${operatorIndex})`
).click();
// Click on field dropdown
cy.get('.rule--field').eq(index_2).should('be.visible').click();
cy.get(fieldid).eq(2).should('be.visible').click();
cy.get(fieldId).eq(2).should('be.visible').click();
// Select the condition
cy.get('.rule--operator').eq(index_2).should('be.visible').click();

View File

@ -11,7 +11,11 @@
* limitations under the License.
*/
import { addOwner, removeOwner } from '../../common/advancedSearch';
import {
addOwner,
goToAdvanceSearch,
removeOwner,
} from '../../common/advancedSearch';
import { searchAndClickOnOption } from '../../common/advancedSearchQuickFilters';
import { interceptURL, verifyResponseStatusCode } from '../../common/common';
import { QUICK_FILTERS_BY_ASSETS } from '../../constants/advancedSearchQuickFilters.constants';
@ -78,10 +82,7 @@ describe(`Advanced search quick filters should work properly for assets`, () =>
});
const testIsNullAndIsNotNullFilters = (operatorTitle, queryFilter, alias) => {
cy.sidebarClick(SidebarItem.EXPLORE);
const asset = QUICK_FILTERS_BY_ASSETS[0];
cy.get(`[data-testid="${asset.tab}"]`).scrollIntoView().click();
cy.get('[data-testid="advance-search-button"]').click();
goToAdvanceSearch();
// Check Is Null or Is Not Null
cy.get('.rule--operator > .ant-select > .ant-select-selector').eq(0).click();

View File

@ -68,9 +68,12 @@ describe('Advance search', () => {
checkmustPaths(
condition.name,
field.testid,
Cypress._.toLower(field.searchCriteriaFirstGroup),
field.isLocalSearch
? field.searchCriteriaFirstGroup
: Cypress._.toLower(field.searchCriteriaFirstGroup),
0,
field.responseValueFirstGroup
field.responseValueFirstGroup,
field.isLocalSearch
);
});
@ -78,9 +81,12 @@ describe('Advance search', () => {
checkmust_notPaths(
condition.name,
field.testid,
Cypress._.toLower(field.searchCriteriaFirstGroup),
field.isLocalSearch
? field.searchCriteriaFirstGroup
: Cypress._.toLower(field.searchCriteriaFirstGroup),
0,
field.responseValueFirstGroup
field.responseValueFirstGroup,
field.isLocalSearch
);
});
});
@ -108,15 +114,20 @@ describe('Advance search', () => {
CONDITIONS_MUST.equalTo.name,
CONDITIONS_MUST_NOT.notEqualTo.name,
field.testid,
Cypress._.toLower(field.searchCriteriaFirstGroup),
Cypress._.toLower(field.searchCriteriaSecondGroup),
field.isLocalSearch
? field.searchCriteriaFirstGroup
: Cypress._.toLower(field.searchCriteriaFirstGroup),
field.isLocalSearch
? field.searchCriteriaSecondGroup
: Cypress._.toLower(field.searchCriteriaSecondGroup),
0,
1,
operator.index,
CONDITIONS_MUST.equalTo.filter,
CONDITIONS_MUST_NOT.notEqualTo.filter,
field.responseValueFirstGroup,
val
val,
field.isLocalSearch
);
});
});
@ -131,15 +142,20 @@ describe('Advance search', () => {
CONDITIONS_MUST.anyIn.name,
CONDITIONS_MUST_NOT.notIn.name,
field.testid,
Cypress._.toLower(field.searchCriteriaFirstGroup),
Cypress._.toLower(field.searchCriteriaSecondGroup),
field.isLocalSearch
? field.searchCriteriaFirstGroup
: Cypress._.toLower(field.searchCriteriaFirstGroup),
field.isLocalSearch
? field.searchCriteriaSecondGroup
: Cypress._.toLower(field.searchCriteriaSecondGroup),
0,
1,
operator.index,
CONDITIONS_MUST.anyIn.filter,
CONDITIONS_MUST_NOT.notIn.filter,
field.responseValueFirstGroup,
val
val,
field.isLocalSearch
);
});
});
@ -152,15 +168,20 @@ describe('Advance search', () => {
CONDITIONS_MUST.contains.name,
CONDITIONS_MUST_NOT.notContains.name,
field.testid,
Cypress._.toLower(field.searchCriteriaFirstGroup),
Cypress._.toLower(field.searchCriteriaSecondGroup),
field.isLocalSearch
? field.searchCriteriaFirstGroup
: Cypress._.toLower(field.searchCriteriaFirstGroup),
field.isLocalSearch
? field.searchCriteriaSecondGroup
: Cypress._.toLower(field.searchCriteriaSecondGroup),
0,
1,
operator.index,
CONDITIONS_MUST.contains.filter,
CONDITIONS_MUST_NOT.notContains.filter,
field.responseValueFirstGroup,
val
val,
field.isLocalSearch
);
});
});

View File

@ -12,18 +12,9 @@
*/
import { Button, Modal, Space, Typography } from 'antd';
import { cloneDeep } from 'lodash';
import React, { FunctionComponent, useEffect } from 'react';
import {
Builder,
FieldGroup,
Query,
ValueSource,
} from 'react-awesome-query-builder';
import React, { FunctionComponent } from 'react';
import { Builder, Query } from 'react-awesome-query-builder';
import { useTranslation } from 'react-i18next';
import { getTypeByFQN } from '../../rest/metadataTypeAPI';
import { EntitiesSupportedCustomProperties } from '../../utils/CustomProperties/CustomProperty.utils';
import { getEntityTypeFromSearchIndex } from '../../utils/SearchUtils';
import './advanced-search-modal.less';
import { useAdvanceSearch } from './AdvanceSearchProvider/AdvanceSearchProvider.component';
@ -39,51 +30,7 @@ export const AdvancedSearchModal: FunctionComponent<Props> = ({
onCancel,
}: Props) => {
const { t } = useTranslation();
const {
config,
treeInternal,
onTreeUpdate,
onReset,
onUpdateConfig,
searchIndex,
} = useAdvanceSearch();
const updatedConfig = cloneDeep(config);
async function getCustomAttributesSubfields() {
try {
const entityType = getEntityTypeFromSearchIndex(searchIndex);
if (!entityType) {
return;
}
const res = await getTypeByFQN(entityType);
const customAttributes = res.customProperties;
const subfields: Record<
string,
{ type: string; valueSources: ValueSource[] }
> = {};
if (customAttributes) {
customAttributes.forEach((attr) => {
subfields[attr.name] = {
type: 'text',
valueSources: ['value'],
};
});
}
(updatedConfig.fields.extension as FieldGroup).subfields = subfields;
onUpdateConfig(updatedConfig);
} catch (error) {
// Error
}
}
useEffect(() => {
if (visible && EntitiesSupportedCustomProperties.includes(searchIndex)) {
getCustomAttributesSubfields();
}
}, [visible, searchIndex]);
const { config, treeInternal, onTreeUpdate, onReset } = useAdvanceSearch();
return (
<Modal

View File

@ -1,5 +1,5 @@
/*
* Copyright 2023 Collate.
* Copyright 2024 Collate.
* Licensed 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
@ -10,8 +10,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { cloneDeep, isNil, isString } from 'lodash';
import { isEmpty, isEqual, isNil, isString } from 'lodash';
import Qs from 'qs';
import React, {
useCallback,
@ -35,6 +34,8 @@ import {
} from '../../../constants/AdvancedSearch.constants';
import { SearchIndex } from '../../../enums/search.enum';
import { getTypeByFQN } from '../../../rest/metadataTypeAPI';
import { getTierOptions } from '../../../utils/AdvancedSearchUtils';
import { EntitiesSupportedCustomProperties } from '../../../utils/CustomProperties/CustomProperty.utils';
import { elasticSearchFormat } from '../../../utils/QueryBuilderElasticsearchFormatUtils';
import searchClassBase from '../../../utils/SearchClassBase';
import { getEntityTypeFromSearchIndex } from '../../../utils/SearchUtils';
@ -53,6 +54,8 @@ const AdvancedSearchContext = React.createContext<AdvanceSearchContext>(
export const AdvanceSearchProvider = ({
children,
}: AdvanceSearchProviderProps) => {
const tierOptions = useMemo(getTierOptions, []);
const tabsInfo = searchClassBase.getTabsInfo();
const location = useLocation();
const history = useHistory();
@ -68,7 +71,10 @@ export const AdvanceSearchProvider = ({
return tabInfo[0] as ExploreSearchIndex;
}, [tab]);
const [config, setConfig] = useState<Config>(getQbConfigs(searchIndex));
const [config, setConfig] = useState<Config>(
getQbConfigs(searchIndex, tierOptions)
);
const [initialised, setInitialised] = useState(false);
const defaultTree = useMemo(
() => QbUtils.checkTree(QbUtils.loadTree(emptyJsonTree), config),
@ -117,7 +123,7 @@ export const AdvanceSearchProvider = ({
);
useEffect(() => {
setConfig(getQbConfigs(searchIndex));
setConfig(getQbConfigs(searchIndex, tierOptions));
}, [searchIndex]);
const handleChange = useCallback(
@ -150,7 +156,7 @@ export const AdvanceSearchProvider = ({
setTreeInternal(QbUtils.checkTree(QbUtils.loadTree(emptyJsonTree), config));
setQueryFilter(undefined);
setSQLQuery('');
}, []);
}, [config]);
const handleConfigUpdate = (updatedConfig: Config) => {
setConfig(updatedConfig);
@ -171,20 +177,24 @@ export const AdvanceSearchProvider = ({
}, [history, location.pathname]);
async function getCustomAttributesSubfields() {
const updatedConfig = cloneDeep(config);
try {
const entityType = getEntityTypeFromSearchIndex(searchIndex);
if (!entityType) {
return;
}
const res = await getTypeByFQN(entityType);
const customAttributes = res.customProperties;
const subfields: Record<
string,
{ type: string; valueSources: ValueSource[] }
> = {};
try {
if (!EntitiesSupportedCustomProperties.includes(searchIndex)) {
return subfields;
}
const entityType = getEntityTypeFromSearchIndex(searchIndex);
if (!entityType) {
return subfields;
}
const res = await getTypeByFQN(entityType);
const customAttributes = res.customProperties;
if (customAttributes) {
customAttributes.forEach((attr) => {
subfields[attr.name] = {
@ -193,30 +203,55 @@ export const AdvanceSearchProvider = ({
};
});
}
(updatedConfig.fields.extension as FieldGroup).subfields = subfields;
return updatedConfig;
return subfields;
} catch (error) {
// Error
return updatedConfig;
return subfields;
}
}
const loadData = async () => {
const actualConfig = getQbConfigs(searchIndex, tierOptions);
const extensionSubField = await getCustomAttributesSubfields();
if (!isEmpty(extensionSubField)) {
(actualConfig.fields.extension as FieldGroup).subfields =
extensionSubField;
}
setConfig(actualConfig);
setInitialised(true);
};
const loadTree = useCallback(
async (treeObj: JsonTree) => {
const updatedConfig = (await getCustomAttributesSubfields()) ?? config;
const updatedConfig = config;
const tree = QbUtils.checkTree(QbUtils.loadTree(treeObj), updatedConfig);
setTreeInternal(tree);
const qFilter = {
query: elasticSearchFormat(tree, updatedConfig),
};
if (isEqual(qFilter, queryFilter)) {
return;
}
setQueryFilter(qFilter);
setSQLQuery(QbUtils.sqlFormat(tree, updatedConfig) ?? '');
},
[config]
[config, queryFilter]
);
useEffect(() => {
loadData();
}, [searchIndex]);
useEffect(() => {
if (!initialised) {
return;
}
if (jsonTree) {
loadTree(jsonTree);
} else {
@ -224,7 +259,7 @@ export const AdvanceSearchProvider = ({
}
setLoading(false);
}, [jsonTree]);
}, [jsonTree, initialised]);
const handleSubmit = useCallback(() => {
const qFilter = {

View File

@ -0,0 +1,124 @@
/*
* Copyright 2024 Collate.
* Licensed 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 { act, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import {
AdvanceSearchProvider,
useAdvanceSearch,
} from './AdvanceSearchProvider.component';
jest.mock('../../../rest/metadataTypeAPI', () => ({
getTypeByFQN: jest.fn().mockResolvedValue({}),
}));
jest.mock('../../../rest/tagAPI', () => ({
getTags: jest.fn().mockResolvedValue({}),
}));
jest.mock('../AdvanceSearchModal.component', () => ({
AdvancedSearchModal: jest
.fn()
.mockImplementation(({ visible, onSubmit, onCancel }) => (
<>
{visible ? (
<p>AdvanceSearchModal Open</p>
) : (
<p>AdvanceSearchModal Close</p>
)}
<button onClick={onSubmit}>Apply Advance Search</button>
<button onClick={onCancel}>Close Modal</button>
</>
)),
}));
jest.mock('../../Loader/Loader', () =>
jest.fn().mockReturnValue(<div>Loader</div>)
);
const mockPush = jest.fn();
jest.mock('react-router-dom', () => ({
useLocation: jest.fn().mockReturnValue({
search: 'queryFilter={"some":"value"}',
}),
useParams: jest.fn().mockReturnValue({
tab: 'tabValue',
}),
useHistory: jest.fn().mockImplementation(() => ({
push: mockPush,
})),
}));
const Children = () => {
const { toggleModal, onResetAllFilters } = useAdvanceSearch();
return (
<>
<button onClick={() => toggleModal(true)}>
Open AdvanceSearch Modal
</button>
<button onClick={onResetAllFilters}>Reset All Filters</button>
</>
);
};
const mockWithAdvanceSearch =
(Component: React.FC) =>
(props: JSX.IntrinsicAttributes & { children?: React.ReactNode }) => {
return (
<AdvanceSearchProvider>
<Component {...props} />
</AdvanceSearchProvider>
);
};
const ComponentWithProvider = mockWithAdvanceSearch(Children);
describe('AdvanceSearchProvider component', () => {
it('should render the AdvanceSearchModal as close by default', () => {
render(<ComponentWithProvider />);
expect(screen.getByText('AdvanceSearchModal Close')).toBeInTheDocument();
});
it('should call mockPush after submit advance search form', async () => {
render(<ComponentWithProvider />);
userEvent.click(screen.getByText('Apply Advance Search'));
expect(mockPush).toHaveBeenCalled();
});
it('should open the AdvanceSearchModal on call of toggleModal with true', async () => {
await act(async () => {
render(<ComponentWithProvider />);
});
expect(screen.getByText('AdvanceSearchModal Close')).toBeInTheDocument();
userEvent.click(screen.getByText('Open AdvanceSearch Modal'));
expect(screen.getByText('AdvanceSearchModal Open')).toBeInTheDocument();
});
it('onResetAllFilters call mockPush should be called', async () => {
await act(async () => {
render(<ComponentWithProvider />);
});
userEvent.click(screen.getByText('Reset All Filters'));
expect(mockPush).toHaveBeenCalled();
});
});

View File

@ -219,7 +219,7 @@ const ExploreV1: React.FC<ExploreProps> = ({
handleSummaryPanelDisplay(
highlightEntityNameAndDescription(
firstEntity._source,
firstEntity.highlight
firstEntity?.highlight
)
);
} else {
@ -356,7 +356,7 @@ const ExploreV1: React.FC<ExploreProps> = ({
handleClosePanel={handleClosePanel}
highlights={omit(
{
...firstEntity.highlight, // highlights of firstEntity that we get from the query api
...firstEntity?.highlight, // highlights of firstEntity that we get from the query api
'tag.name': (
selectedQuickFilters?.find(
(filterOption) => filterOption.key === TAG_FQN_KEY

View File

@ -13,9 +13,12 @@
import { t } from 'i18next';
import {
AsyncFetchListValues,
AsyncFetchListValuesResult,
BasicConfig,
Fields,
JsonTree,
ListItem,
SelectFieldSettings,
Utils as QbUtils,
} from 'react-awesome-query-builder';
@ -304,6 +307,23 @@ export const autocomplete: (args: {
};
};
export const autoCompleteTier: (
tierOptions: Promise<AsyncFetchListValues>
) => SelectFieldSettings['asyncFetch'] = (tierOptions) => {
return async (search) => {
const resolvedTierOptions = (await tierOptions) as ListItem[];
return {
values: !search
? resolvedTierOptions
: resolvedTierOptions.filter((tier) =>
tier.title?.toLowerCase()?.includes(search.toLowerCase())
),
hasMore: false,
} as AsyncFetchListValuesResult;
};
};
const mainWidgetProps = {
fullWidth: true,
valueLabel: t('label.criteria') + ':',
@ -313,7 +333,8 @@ const mainWidgetProps = {
* Common fields that exit for all searchable entities
*/
const getCommonQueryBuilderFields = (
entitySearchIndex: SearchIndex = SearchIndex.TABLE
entitySearchIndex: SearchIndex = SearchIndex.TABLE,
tierOptions: Promise<AsyncFetchListValues> = Promise.resolve([])
) => {
const commonQueryBuilderFields: Fields = {
deleted: {
@ -356,10 +377,7 @@ const getCommonQueryBuilderFields = (
type: 'select',
mainWidgetProps,
fieldSettings: {
asyncFetch: autocomplete({
searchIndex: entitySearchIndex ?? [SearchIndex.TAG],
entityField: EntityFields.TIER,
}),
asyncFetch: autoCompleteTier(tierOptions),
useAsyncSearch: true,
},
},
@ -522,15 +540,16 @@ const getInitialConfigWithoutFields = () => {
/**
* Builds search index specific configuration for the query builder
*/
export const getQbConfigs: (searchIndex: SearchIndex) => BasicConfig = (
searchIndex
) => {
export const getQbConfigs: (
searchIndex: SearchIndex,
tierOptions: Promise<AsyncFetchListValues>
) => BasicConfig = (searchIndex, tierOptions) => {
switch (searchIndex) {
case SearchIndex.MLMODEL:
return {
...getInitialConfigWithoutFields(),
fields: {
...getCommonQueryBuilderFields(SearchIndex.MLMODEL),
...getCommonQueryBuilderFields(SearchIndex.MLMODEL, tierOptions),
...getServiceQueryBuilderFields(SearchIndex.MLMODEL),
},
};
@ -539,7 +558,7 @@ export const getQbConfigs: (searchIndex: SearchIndex) => BasicConfig = (
return {
...getInitialConfigWithoutFields(),
fields: {
...getCommonQueryBuilderFields(SearchIndex.PIPELINE),
...getCommonQueryBuilderFields(SearchIndex.PIPELINE, tierOptions),
...getServiceQueryBuilderFields(SearchIndex.PIPELINE),
},
};
@ -548,7 +567,7 @@ export const getQbConfigs: (searchIndex: SearchIndex) => BasicConfig = (
return {
...getInitialConfigWithoutFields(),
fields: {
...getCommonQueryBuilderFields(SearchIndex.DASHBOARD),
...getCommonQueryBuilderFields(SearchIndex.DASHBOARD, tierOptions),
...getServiceQueryBuilderFields(SearchIndex.DASHBOARD),
},
};
@ -557,7 +576,7 @@ export const getQbConfigs: (searchIndex: SearchIndex) => BasicConfig = (
return {
...getInitialConfigWithoutFields(),
fields: {
...getCommonQueryBuilderFields(SearchIndex.TABLE),
...getCommonQueryBuilderFields(SearchIndex.TABLE, tierOptions),
...getServiceQueryBuilderFields(SearchIndex.TABLE),
...tableQueryBuilderFields,
},
@ -567,7 +586,7 @@ export const getQbConfigs: (searchIndex: SearchIndex) => BasicConfig = (
return {
...getInitialConfigWithoutFields(),
fields: {
...getCommonQueryBuilderFields(SearchIndex.TOPIC),
...getCommonQueryBuilderFields(SearchIndex.TOPIC, tierOptions),
...getServiceQueryBuilderFields(SearchIndex.TOPIC),
},
};

View File

@ -16,7 +16,10 @@ import { Button, Checkbox, MenuProps, Space, Typography } from 'antd';
import i18next from 'i18next';
import { isArray, isEmpty } from 'lodash';
import React from 'react';
import { RenderSettings } from 'react-awesome-query-builder';
import {
AsyncFetchListValues,
RenderSettings,
} from 'react-awesome-query-builder';
import ProfilePicture from '../components/common/ProfilePicture/ProfilePicture';
import { AssetsOfEntity } from '../components/Glossary/GlossaryTerms/tabs/AssetsTabs.interface';
import { SearchDropdownOption } from '../components/SearchDropdown/SearchDropdown.interface';
@ -38,6 +41,7 @@ import {
TableSearchSource,
TopicSearchSource,
} from '../interface/search.interface';
import { getTags } from '../rest/tagAPI';
import { getCountBadge } from '../utils/CommonUtils';
import { getEntityName } from './EntityUtils';
import searchClassBase from './SearchClassBase';
@ -393,3 +397,20 @@ export const getOptionsFromAggregationBucket = (buckets: Bucket[]) => {
count: option.doc_count ?? 0,
}));
};
export const getTierOptions: () => Promise<AsyncFetchListValues> = async () => {
try {
const { data: tiers } = await getTags({
parent: 'Tier',
});
const tierFields = tiers.map((tier) => ({
title: tier.fullyQualifiedName, // tier.name,
value: tier.fullyQualifiedName,
}));
return tierFields;
} catch (error) {
return [];
}
};