mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-10-04 05:16:47 +00:00
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:
parent
2d58db69d2
commit
78839892b6
@ -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!');
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,5 +15,5 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
align-items: end;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
@ -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,
|
||||
}}
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
];
|
||||
|
@ -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,
|
||||
];
|
@ -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',
|
||||
}
|
||||
|
@ -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'],
|
||||
|
@ -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),
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user