modify the rule for dataAssetRule and semantic form contract (#22744)

* modify the rule for dataAssetRule and semantic form contract

* fix the query input placement

* fix the rule config

* update logic to add specific fields

* update json logic to get fields

* address comments

* fix tier option logic

* fix delete data contract test

---------

Co-authored-by: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com>
Co-authored-by: Pranita <pfulsundar8@gmail.com>
This commit is contained in:
Ashish Gupta 2025-08-06 14:53:20 +05:30 committed by GitHub
parent 2d58db69d2
commit 78839892b6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 422 additions and 164 deletions

View File

@ -414,6 +414,19 @@ test.describe('Data Contracts', () => {
);
await page.getByTestId('delete-contract-button').click();
await expect(
page
.locator('.ant-modal-title')
.getByText(`Delete dataContract "${DATA_CONTRACT_DETAILS.name}"`)
).toBeVisible();
await page.getByTestId('confirmation-text-input').click();
await page.getByTestId('confirmation-text-input').fill('DELETE');
await expect(page.getByTestId('confirm-button')).toBeEnabled();
await page.getByTestId('confirm-button').click();
await deleteContractResponse;
await toastNotification(page, '"Contract" deleted successfully!');

View File

@ -31,6 +31,7 @@ import { useTranslation } from 'react-i18next';
import { ReactComponent as AddPlaceHolderIcon } from '../../assets/svg/add-placeholder.svg';
import { ReactComponent as IconEdit } from '../../assets/svg/edit-new.svg';
import { ReactComponent as IconDelete } from '../../assets/svg/ic-delete.svg';
import { EntityReferenceFields } from '../../enums/AdvancedSearch.enum';
import { SIZE } from '../../enums/common.enum';
import {
ProviderType,
@ -43,6 +44,7 @@ import {
updateSettingsConfig,
} from '../../rest/settingConfigAPI';
import i18n, { t } from '../../utils/i18next/LocalUtil';
import jsonLogicSearchClassBase from '../../utils/JSONLogicSearchClassBase';
import { showErrorToast, showSuccessToast } from '../../utils/ToastUtils';
import QueryBuilderWidget from '../common/Form/JSONSchema/JsonSchemaWidgets/QueryBuilderWidget/QueryBuilderWidget';
import RichTextEditorPreviewerNew from '../common/RichTextEditor/RichTextEditorPreviewNew';
@ -134,6 +136,18 @@ export const SemanticsRuleForm: React.FC<{
form.setFieldsValue(semanticsRule);
}, [semanticsRule]);
const queryBuilderFields = useMemo(() => {
const fields = jsonLogicSearchClassBase.getMapFields();
return {
[EntityReferenceFields.TAG]: fields[EntityReferenceFields.TAG],
[EntityReferenceFields.TIER]: fields[EntityReferenceFields.TIER],
[EntityReferenceFields.DOMAIN]: fields[EntityReferenceFields.DOMAIN],
[EntityReferenceFields.DATA_PRODUCT]:
fields[EntityReferenceFields.DATA_PRODUCT],
};
}, []);
return (
<Form form={form} layout="vertical">
<Form.Item
@ -167,7 +181,6 @@ export const SemanticsRuleForm: React.FC<{
<Input.TextArea placeholder={t('label.description')} rows={2} />
</Form.Item>
<Form.Item
label={t('label.rule')}
name="rule"
rules={[
{
@ -176,9 +189,13 @@ export const SemanticsRuleForm: React.FC<{
]}>
{/* @ts-expect-error because Form.Item will provide value and onChange */}
<QueryBuilderWidget
defaultField={EntityReferenceFields.TAG}
fields={queryBuilderFields}
label={t('label.rule')}
schema={{
outputType: SearchOutputType.JSONLogic,
}}
subField="tagFQN"
/>
</Form.Item>
</Form>

View File

@ -223,6 +223,9 @@
}
}
.ant-form-item-control-input-content {
.group--field {
align-self: baseline;
}
input[type='text'] {
padding: 8px 14px;
color: @grey-700;
@ -239,7 +242,7 @@
border: 1px solid @grey-300;
padding: 4px 12px;
color: @grey-700;
height: 40px;
min-height: 40px;
box-shadow: 0 1px 2px 0px @grey-27;
&:focus,
@ -318,9 +321,18 @@
height: 40px !important;
box-shadow: 0 1px 2px 0px @grey-27;
&:focus,
&:hover {
border-color: @primary-color;
.ant-select-selector,
.ant-mentions {
border: 1px solid @grey-300;
padding: 4px 12px !important;
color: @grey-700;
min-height: 40px !important;
box-shadow: 0 1px 2px 0px @grey-27;
&:focus,
&:hover {
border-color: @primary-color;
}
}
}
}

View File

@ -15,5 +15,5 @@
display: flex;
flex-direction: column;
gap: 16px;
align-items: end;
align-items: flex-end;
}

View File

@ -19,14 +19,16 @@ import Card from 'antd/lib/card/Card';
import TextArea from 'antd/lib/input/TextArea';
import classNames from 'classnames';
import { isNull } from 'lodash';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { ReactComponent as DeleteIcon } from '../../../assets/svg/ic-trash.svg';
import { ReactComponent as LeftOutlined } from '../../../assets/svg/left-arrow.svg';
import { ReactComponent as RightIcon } from '../../../assets/svg/right-arrow.svg';
import { ReactComponent as PlusIcon } from '../../../assets/svg/x-colored.svg';
import { EntityReferenceFields } from '../../../enums/AdvancedSearch.enum';
import { EntityType } from '../../../enums/entity.enum';
import { DataContract } from '../../../generated/entity/data/dataContract';
import jsonLogicSearchClassBase from '../../../utils/JSONLogicSearchClassBase';
import ExpandableCard from '../../common/ExpandableCard/ExpandableCard';
import QueryBuilderWidget from '../../common/Form/JSONSchema/JsonSchemaWidgets/QueryBuilderWidget/QueryBuilderWidget';
import { EditIconButton } from '../../common/IconButtons/EditIconButton';
@ -105,6 +107,15 @@ export const ContractSemanticFormTab: React.FC<{
setEditingKey(0);
}, [initialValues]);
// Remove extension field from common config
const queryBuilderFields = useMemo(() => {
const fields = jsonLogicSearchClassBase.getCommonConfig();
delete fields[EntityReferenceFields.EXTENSION];
return fields;
}, []);
return (
<>
<Card className="contract-semantic-form-container container bg-grey p-box">
@ -253,6 +264,7 @@ export const ContractSemanticFormTab: React.FC<{
name={[field.name, 'rule']}>
{/* @ts-expect-error because Form.Item will provide value and onChange */}
<QueryBuilderWidget
fields={queryBuilderFields}
formContext={{
entityType: EntityType.TABLE,
}}
@ -299,6 +311,7 @@ export const ContractSemanticFormTab: React.FC<{
{/* @ts-expect-error because Form.Item will provide value and onChange */}
<QueryBuilderWidget
readonly
fields={queryBuilderFields}
formContext={{
entityType: EntityType.TABLE,
}}

View File

@ -22,6 +22,7 @@ import {
getContractByEntityId,
} from '../../../rest/contractAPI';
import { showErrorToast, showSuccessToast } from '../../../utils/ToastUtils';
import DeleteWidgetModal from '../../common/DeleteWidget/DeleteWidgetModal';
import Loader from '../../common/Loader/Loader';
import { useGenericContext } from '../../Customization/GenericProvider/GenericProvider';
import AddDataContract from '../AddDataContract/AddDataContract';
@ -38,6 +39,7 @@ export const ContractTab = () => {
);
const [contract, setContract] = useState<DataContract>();
const [isLoading, setIsLoading] = useState(true);
const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false);
const fetchContract = async () => {
try {
@ -55,21 +57,30 @@ export const ContractTab = () => {
const handleDelete = async () => {
if (contract?.id) {
try {
await deleteContractById(contract.id);
showSuccessToast(
t('server.entity-deleted-successfully', {
entity: t('label.contract'),
})
);
fetchContract();
setTabMode(DataContractTabMode.VIEW);
} catch (err) {
showErrorToast(err as AxiosError);
}
setIsDeleteModalVisible(true);
}
};
const handleContractDeleteConfirm = async () => {
if (!contract?.id) {
return;
}
try {
await deleteContractById(contract.id);
showSuccessToast(
t('server.entity-deleted-successfully', {
entity: t('label.contract'),
})
);
fetchContract();
setTabMode(DataContractTabMode.VIEW);
} catch (error) {
showErrorToast(error as AxiosError);
}
setIsDeleteModalVisible(false);
};
useEffect(() => {
fetchContract();
}, [id]);
@ -122,6 +133,18 @@ export const ContractTab = () => {
return isLoading ? (
<Loader />
) : (
<div className="contract-tab-container">{content}</div>
<div className="contract-tab-container">
{content}
<DeleteWidgetModal
allowSoftDelete={false}
entityName={contract?.name ?? ''}
entityType={EntityType.DATA_CONTRACT}
visible={isDeleteModalVisible}
onCancel={() => {
setIsDeleteModalVisible(false);
}}
onDelete={handleContractDeleteConfirm}
/>
</div>
);
};

View File

@ -60,12 +60,13 @@ import { useAdvanceSearch } from '../../../../../Explore/AdvanceSearchProvider/A
import { SearchOutputType } from '../../../../../Explore/AdvanceSearchProvider/AdvanceSearchProvider.interface';
import './query-builder-widget.less';
const QueryBuilderWidget: FC<WidgetProps> = ({
onChange,
schema,
value,
...props
}) => {
const QueryBuilderWidget: FC<
WidgetProps & {
fields?: Config['fields'];
defaultField?: string;
subField?: string;
}
> = ({ onChange, schema, value, fields, defaultField, subField, ...props }) => {
const {
config,
treeInternal,
@ -202,7 +203,7 @@ const QueryBuilderWidget: FC<WidgetProps> = ({
} else {
const emptyJsonTree =
outputType === SearchOutputType.JSONLogic
? getEmptyJsonTreeForQueryBuilder()
? getEmptyJsonTreeForQueryBuilder(defaultField, subField)
: getEmptyJsonTree();
const tree = QbUtils.loadTree(emptyJsonTree);
@ -253,6 +254,7 @@ const QueryBuilderWidget: FC<WidgetProps> = ({
)}
<Query
{...config}
fields={fields ?? config.fields}
renderBuilder={(props) => {
// Store the actions for external access
if (!queryActions) {

View File

@ -11,6 +11,8 @@
* limitations under the License.
*/
import { EntityReferenceFields } from '../enums/AdvancedSearch.enum';
export enum DataContractMode {
YAML,
UI,
@ -28,3 +30,14 @@ export enum EDataContractTab {
SEMANTICS,
QUALITY,
}
export const DATA_ASSET_RULE_FIELDS_NOT_TO_RENDER = [
EntityReferenceFields.EXTENSION,
EntityReferenceFields.OWNERS,
EntityReferenceFields.NAME,
EntityReferenceFields.DESCRIPTION,
EntityReferenceFields.TIER,
EntityReferenceFields.SERVICE,
EntityReferenceFields.DISPLAY_NAME,
EntityReferenceFields.DELETED,
];

View File

@ -0,0 +1,37 @@
/*
* Copyright 2025 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 { EntityReferenceFields } from '../enums/AdvancedSearch.enum';
export const GLOSSARY_ENTITY_FIELDS_KEYS: EntityReferenceFields[] = [
EntityReferenceFields.REVIEWERS,
EntityReferenceFields.UPDATED_BY,
];
export const TABLE_ENTITY_FIELDS_KEYS: EntityReferenceFields[] = [
EntityReferenceFields.DATABASE,
EntityReferenceFields.DATABASE_SCHEMA,
EntityReferenceFields.TABLE_TYPE,
];
export const COMMON_ENTITY_FIELDS_KEYS: EntityReferenceFields[] = [
EntityReferenceFields.SERVICE,
EntityReferenceFields.OWNERS,
EntityReferenceFields.DISPLAY_NAME,
EntityReferenceFields.NAME,
EntityReferenceFields.DESCRIPTION,
EntityReferenceFields.TAG,
EntityReferenceFields.DOMAIN,
EntityReferenceFields.DATA_PRODUCT,
EntityReferenceFields.TIER,
EntityReferenceFields.EXTENSION,
];

View File

@ -96,9 +96,12 @@ export enum EntityReferenceFields {
DISPLAY_NAME = 'displayName',
TAG = 'tags',
TIER = 'tier.tagFQN',
DOMAIN = 'domain',
DATA_PRODUCT = 'dataProduct',
TABLE_TYPE = 'tableType',
EXTENSION = 'extension',
SERVICE = 'service.name',
UPDATED_BY = 'updatedBy',
CHANGE_DESCRIPTION = 'changeDescription',
DELETED = 'deleted',
}

View File

@ -454,7 +454,8 @@ export const getEmptyJsonTree = (
* This structure allows easy addition of groups and rules
*/
export const getEmptyJsonTreeForQueryBuilder = (
defaultField: string = EntityReferenceFields.OWNERS
defaultField: string = EntityReferenceFields.OWNERS,
subField = 'fullyQualifiedName'
): OldJsonTree => {
const uuid1 = QbUtils.uuid();
const uuid2 = QbUtils.uuid();
@ -483,7 +484,7 @@ export const getEmptyJsonTreeForQueryBuilder = (
type: 'rule',
id: uuid3,
properties: {
field: 'owners.fullyQualifiedName',
field: `${defaultField}.${subField}`,
operator: 'select_equals',
value: [],
valueSrc: ['value'],

View File

@ -12,24 +12,36 @@
*/
import {
AntdConfig,
AsyncFetchListValuesResult,
Config,
FieldOrGroup,
Fields,
ListItem,
ListValues,
Operators,
SelectFieldSettings,
} from '@react-awesome-query-builder/antd';
import { get, sortBy } from 'lodash';
import { get, sortBy, toLower } from 'lodash';
import { TEXT_FIELD_OPERATORS } from '../constants/AdvancedSearch.constants';
import { PAGE_SIZE_BASE } from '../constants/constants';
import {
COMMON_ENTITY_FIELDS_KEYS,
GLOSSARY_ENTITY_FIELDS_KEYS,
TABLE_ENTITY_FIELDS_KEYS,
} from '../constants/JSONLogicSearch.constants';
import {
EntityFields,
EntityReferenceFields,
} from '../enums/AdvancedSearch.enum';
import { SearchIndex } from '../enums/search.enum';
import { searchData } from '../rest/miscAPI';
import { getTags } from '../rest/tagAPI';
import advancedSearchClassBase from './AdvancedSearchClassBase';
import { t } from './i18next/LocalUtil';
import { renderJSONLogicQueryBuilderButtons } from './QueryBuilderUtils';
import {
getFieldsByKeys,
renderJSONLogicQueryBuilderButtons,
} from './QueryBuilderUtils';
class JSONLogicSearchClassBase {
baseConfig = AntdConfig as Config;
@ -143,6 +155,8 @@ class JSONLogicSearchClassBase {
},
};
mapFields: Record<string, FieldOrGroup>;
defaultSelectOperators = [
'select_equals',
'select_not_equals',
@ -152,135 +166,8 @@ class JSONLogicSearchClassBase {
'is_not_null',
];
public searchAutocomplete: (args: {
searchIndex: SearchIndex | SearchIndex[];
fieldName: string;
fieldLabel: string;
}) => SelectFieldSettings['asyncFetch'] = ({
searchIndex,
fieldName,
fieldLabel,
}) => {
return (search) => {
return searchData(
Array.isArray(search) ? search.join(',') : search ?? '',
1,
PAGE_SIZE_BASE,
'',
'',
'',
searchIndex ?? SearchIndex.DATA_ASSET,
false,
false,
false
).then((response) => {
const data = response.data.hits.hits;
return {
values: data.map((item) => ({
value: get(item._source, fieldName, ''),
title: get(item._source, fieldLabel, ''),
})),
hasMore: false,
};
});
};
};
mainWidgetProps = {
fullWidth: true,
valueLabel: t('label.criteria') + ':',
};
glossaryEntityFields: Fields = {
[EntityReferenceFields.REVIEWERS]: {
label: t('label.reviewer-plural'),
type: '!group',
mode: 'some',
defaultField: 'fullyQualifiedName',
subfields: {
fullyQualifiedName: {
label: 'Reviewers New',
type: 'select',
mainWidgetProps: this.mainWidgetProps,
operators: this.defaultSelectOperators,
fieldSettings: {
asyncFetch: advancedSearchClassBase.autocomplete({
searchIndex: [SearchIndex.USER, SearchIndex.TEAM],
entityField: EntityFields.DISPLAY_NAME_KEYWORD,
}),
useAsyncSearch: true,
},
},
},
},
[EntityReferenceFields.UPDATED_BY]: {
label: t('label.updated-by'),
type: 'select',
mainWidgetProps: this.mainWidgetProps,
operators: [...this.defaultSelectOperators, 'isOwner', 'isReviewer'],
fieldSettings: {
asyncFetch: advancedSearchClassBase.autocomplete({
searchIndex: [SearchIndex.USER, SearchIndex.TEAM],
entityField: EntityFields.DISPLAY_NAME_KEYWORD,
}),
useAsyncSearch: true,
},
},
};
tableEntityFields: Fields = {
[EntityReferenceFields.DATABASE]: {
label: t('label.database'),
type: 'select',
mainWidgetProps: this.mainWidgetProps,
operators: this.defaultSelectOperators,
fieldSettings: {
asyncFetch: advancedSearchClassBase.autocomplete({
searchIndex: SearchIndex.TABLE,
entityField: EntityFields.DATABASE_NAME,
isCaseInsensitive: true,
}),
useAsyncSearch: true,
},
},
[EntityReferenceFields.DATABASE_SCHEMA]: {
label: t('label.database-schema'),
type: 'select',
mainWidgetProps: this.mainWidgetProps,
operators: this.defaultSelectOperators,
fieldSettings: {
asyncFetch: advancedSearchClassBase.autocomplete({
searchIndex: SearchIndex.TABLE,
entityField: EntityFields.DATABASE_SCHEMA_NAME,
isCaseInsensitive: true,
}),
useAsyncSearch: true,
},
},
[EntityReferenceFields.TABLE_TYPE]: {
label: t('label.table-type'),
type: 'select',
mainWidgetProps: this.mainWidgetProps,
operators: this.defaultSelectOperators,
fieldSettings: {
asyncFetch: advancedSearchClassBase.autocomplete({
searchIndex: SearchIndex.TABLE,
entityField: EntityFields.TABLE_TYPE,
}),
useAsyncSearch: true,
},
},
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getCommonConfig = (_: {
entitySearchIndex?: Array<SearchIndex>;
tierOptions?: Promise<ListValues>;
}) => {
return {
constructor() {
this.mapFields = {
[EntityReferenceFields.SERVICE]: {
label: t('label.service'),
type: 'select',
@ -375,12 +262,229 @@ class JSONLogicSearchClassBase {
},
},
},
[EntityReferenceFields.DOMAIN]: {
label: t('label.domain'),
type: '!group',
mode: 'some',
defaultField: 'fullyQualifiedName',
subfields: {
fullyQualifiedName: {
label: 'Domain',
type: 'select',
mainWidgetProps: this.mainWidgetProps,
operators: this.defaultSelectOperators,
fieldSettings: {
asyncFetch: this.searchAutocomplete({
searchIndex: SearchIndex.DOMAIN,
fieldName: 'fullyQualifiedName',
fieldLabel: 'name',
}),
useAsyncSearch: true,
},
},
},
},
[EntityReferenceFields.DATA_PRODUCT]: {
label: t('label.data-product'),
type: '!group',
mode: 'some',
defaultField: 'fullyQualifiedName',
subfields: {
fullyQualifiedName: {
label: 'Data Product',
type: 'select',
mainWidgetProps: this.mainWidgetProps,
operators: this.defaultSelectOperators,
fieldSettings: {
asyncFetch: this.searchAutocomplete({
searchIndex: SearchIndex.DATA_PRODUCT,
fieldName: 'fullyQualifiedName',
fieldLabel: 'name',
}),
useAsyncSearch: true,
},
},
},
},
[EntityReferenceFields.TIER]: {
label: t('label.tier'),
type: 'select',
mainWidgetProps: this.mainWidgetProps,
operators: this.defaultSelectOperators,
fieldSettings: {
asyncFetch: this.autoCompleteTier,
useAsyncSearch: true,
},
},
[EntityReferenceFields.EXTENSION]: {
label: t('label.custom-property-plural'),
type: '!struct',
mainWidgetProps: this.mainWidgetProps,
subfields: {},
},
[EntityReferenceFields.DATABASE]: {
label: t('label.database'),
type: 'select',
mainWidgetProps: this.mainWidgetProps,
operators: this.defaultSelectOperators,
fieldSettings: {
asyncFetch: advancedSearchClassBase.autocomplete({
searchIndex: SearchIndex.TABLE,
entityField: EntityFields.DATABASE_NAME,
isCaseInsensitive: true,
}),
useAsyncSearch: true,
},
},
[EntityReferenceFields.DATABASE_SCHEMA]: {
label: t('label.database-schema'),
type: 'select',
mainWidgetProps: this.mainWidgetProps,
operators: this.defaultSelectOperators,
fieldSettings: {
asyncFetch: advancedSearchClassBase.autocomplete({
searchIndex: SearchIndex.TABLE,
entityField: EntityFields.DATABASE_SCHEMA_NAME,
isCaseInsensitive: true,
}),
useAsyncSearch: true,
},
},
[EntityReferenceFields.TABLE_TYPE]: {
label: t('label.table-type'),
type: 'select',
mainWidgetProps: this.mainWidgetProps,
operators: this.defaultSelectOperators,
fieldSettings: {
asyncFetch: advancedSearchClassBase.autocomplete({
searchIndex: SearchIndex.TABLE,
entityField: EntityFields.TABLE_TYPE,
}),
useAsyncSearch: true,
},
},
[EntityReferenceFields.REVIEWERS]: {
label: t('label.reviewer-plural'),
type: '!group',
mode: 'some',
defaultField: 'fullyQualifiedName',
subfields: {
fullyQualifiedName: {
label: 'Reviewers New',
type: 'select',
mainWidgetProps: this.mainWidgetProps,
operators: this.defaultSelectOperators,
fieldSettings: {
asyncFetch: advancedSearchClassBase.autocomplete({
searchIndex: [SearchIndex.USER, SearchIndex.TEAM],
entityField: EntityFields.DISPLAY_NAME_KEYWORD,
}),
useAsyncSearch: true,
},
},
},
},
[EntityReferenceFields.UPDATED_BY]: {
label: t('label.updated-by'),
type: 'select',
mainWidgetProps: this.mainWidgetProps,
operators: [...this.defaultSelectOperators, 'isOwner', 'isReviewer'],
fieldSettings: {
asyncFetch: advancedSearchClassBase.autocomplete({
searchIndex: [SearchIndex.USER, SearchIndex.TEAM],
entityField: EntityFields.DISPLAY_NAME_KEYWORD,
}),
useAsyncSearch: true,
},
},
};
}
public getMapFields = () => {
return this.mapFields;
};
public searchAutocomplete: (args: {
searchIndex: SearchIndex | SearchIndex[];
fieldName: string;
fieldLabel: string;
}) => SelectFieldSettings['asyncFetch'] = ({
searchIndex,
fieldName,
fieldLabel,
}) => {
return (search) => {
return searchData(
Array.isArray(search) ? search.join(',') : search ?? '',
1,
PAGE_SIZE_BASE,
'',
'',
'',
searchIndex ?? SearchIndex.DATA_ASSET,
false,
false,
false
).then((response) => {
const data = response.data.hits.hits;
return {
values: data.map((item) => ({
value: get(item._source, fieldName, ''),
title: get(item._source, fieldLabel, ''),
})),
hasMore: false,
};
});
};
};
mainWidgetProps = {
fullWidth: true,
valueLabel: t('label.criteria') + ':',
};
public autoCompleteTier: SelectFieldSettings['asyncFetch'] = async (
searchOrValues: string | (string | number)[] | null
) => {
let resolvedTierOptions: ListItem[] = [];
try {
const { data: tiers } = await getTags({
parent: 'Tier',
limit: 50,
});
const tierFields = tiers.map((tier) => ({
title: tier.fullyQualifiedName, // tier.name,
value: tier.fullyQualifiedName,
}));
resolvedTierOptions = tierFields as ListItem[];
} catch (error) {
resolvedTierOptions = [];
}
const search = Array.isArray(searchOrValues)
? searchOrValues.join(',')
: searchOrValues;
return {
values: !search
? resolvedTierOptions
: resolvedTierOptions.filter((tier: ListItem) =>
tier.title?.toLowerCase()?.includes(toLower(search))
),
hasMore: false,
} as AsyncFetchListValuesResult;
};
public getCommonConfig = () => {
return {
...getFieldsByKeys(COMMON_ENTITY_FIELDS_KEYS, this.mapFields),
};
};
@ -389,8 +493,14 @@ class JSONLogicSearchClassBase {
): Fields {
let configs: Fields = {};
const configIndexMapping: Partial<Record<SearchIndex, Fields>> = {
[SearchIndex.TABLE]: this.tableEntityFields,
[SearchIndex.GLOSSARY_TERM]: this.glossaryEntityFields,
[SearchIndex.TABLE]: getFieldsByKeys(
TABLE_ENTITY_FIELDS_KEYS,
this.mapFields
),
[SearchIndex.GLOSSARY_TERM]: getFieldsByKeys(
GLOSSARY_ENTITY_FIELDS_KEYS,
this.mapFields
),
};
entitySearchIndex.forEach((index) => {
@ -404,13 +514,12 @@ class JSONLogicSearchClassBase {
*/
public getQueryBuilderFields = ({
entitySearchIndex = [SearchIndex.TABLE],
tierOptions,
}: {
entitySearchIndex?: Array<SearchIndex>;
tierOptions?: Promise<ListValues>;
}) => {
const fieldsConfig = {
...this.getCommonConfig({ entitySearchIndex, tierOptions }),
...this.getCommonConfig(),
...this.getEntitySpecificQueryBuilderFields(entitySearchIndex),
};

View File

@ -864,3 +864,18 @@ export const getEntityTypeAggregationFilter = (
return qFilter;
};
export const getFieldsByKeys = (
keys: EntityReferenceFields[],
mapFields: Record<string, FieldOrGroup>
): Record<string, FieldOrGroup> => {
const filteredFields: Record<string, FieldOrGroup> = {};
keys.forEach((key) => {
if (mapFields[key]) {
filteredFields[key] = mapFields[key];
}
});
return filteredFields;
};