diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataContracts.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataContracts.spec.ts
index 2dcf6d29afe..a9d5a73d5fc 100644
--- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataContracts.spec.ts
+++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataContracts.spec.ts
@@ -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!');
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataAssetRules/DataAssetRules.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataAssetRules/DataAssetRules.component.tsx
index 3d91c5d1121..77e7b51314c 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/DataAssetRules/DataAssetRules.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/DataAssetRules/DataAssetRules.component.tsx
@@ -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 (
{/* @ts-expect-error because Form.Item will provide value and onChange */}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataContract/AddDataContract/add-data-contract.less b/openmetadata-ui/src/main/resources/ui/src/components/DataContract/AddDataContract/add-data-contract.less
index f6b2fe6e85a..c8773132925 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/DataContract/AddDataContract/add-data-contract.less
+++ b/openmetadata-ui/src/main/resources/ui/src/components/DataContract/AddDataContract/add-data-contract.less
@@ -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;
+ }
}
}
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractExecutionChart/contract-execution-chart.less b/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractExecutionChart/contract-execution-chart.less
index cf58ccb6b07..89838076099 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractExecutionChart/contract-execution-chart.less
+++ b/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractExecutionChart/contract-execution-chart.less
@@ -15,5 +15,5 @@
display: flex;
flex-direction: column;
gap: 16px;
- align-items: end;
+ align-items: flex-end;
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractSemanticFormTab/ContractSemanticFormTab.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractSemanticFormTab/ContractSemanticFormTab.tsx
index 2f475d05f85..e23da32fd30 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractSemanticFormTab/ContractSemanticFormTab.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/DataContract/ContractSemanticFormTab/ContractSemanticFormTab.tsx
@@ -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 (
<>
@@ -253,6 +264,7 @@ export const ContractSemanticFormTab: React.FC<{
name={[field.name, 'rule']}>
{/* @ts-expect-error because Form.Item will provide value and onChange */}
{
);
const [contract, setContract] = useState();
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 ? (
) : (
- {content}
+
+ {content}
+ {
+ setIsDeleteModalVisible(false);
+ }}
+ onDelete={handleContractDeleteConfirm}
+ />
+
);
};
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/QueryBuilderWidget/QueryBuilderWidget.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/QueryBuilderWidget/QueryBuilderWidget.tsx
index 7b9f16cb8f3..f28e5df29ec 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/QueryBuilderWidget/QueryBuilderWidget.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/common/Form/JSONSchema/JsonSchemaWidgets/QueryBuilderWidget/QueryBuilderWidget.tsx
@@ -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 = ({
- 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 = ({
} else {
const emptyJsonTree =
outputType === SearchOutputType.JSONLogic
- ? getEmptyJsonTreeForQueryBuilder()
+ ? getEmptyJsonTreeForQueryBuilder(defaultField, subField)
: getEmptyJsonTree();
const tree = QbUtils.loadTree(emptyJsonTree);
@@ -253,6 +254,7 @@ const QueryBuilderWidget: FC = ({
)}
{
// Store the actions for external access
if (!queryActions) {
diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/DataContract.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/DataContract.constants.ts
index e583450b03e..973b7480ed8 100644
--- a/openmetadata-ui/src/main/resources/ui/src/constants/DataContract.constants.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/constants/DataContract.constants.ts
@@ -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,
+];
diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/JSONLogicSearch.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/JSONLogicSearch.constants.ts
new file mode 100644
index 00000000000..4df934e1a3e
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/constants/JSONLogicSearch.constants.ts
@@ -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,
+];
diff --git a/openmetadata-ui/src/main/resources/ui/src/enums/AdvancedSearch.enum.ts b/openmetadata-ui/src/main/resources/ui/src/enums/AdvancedSearch.enum.ts
index 660321e0793..2711b2aeccb 100644
--- a/openmetadata-ui/src/main/resources/ui/src/enums/AdvancedSearch.enum.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/enums/AdvancedSearch.enum.ts
@@ -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',
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchUtils.tsx
index e6f7f4dd4e4..5861756f59a 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchUtils.tsx
@@ -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'],
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/JSONLogicSearchClassBase.ts b/openmetadata-ui/src/main/resources/ui/src/utils/JSONLogicSearchClassBase.ts
index bf44fa19005..cfceb231fbb 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/JSONLogicSearchClassBase.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/JSONLogicSearchClassBase.ts
@@ -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;
+
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;
- tierOptions?: Promise;
- }) => {
- 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> = {
- [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;
tierOptions?: Promise;
}) => {
const fieldsConfig = {
- ...this.getCommonConfig({ entitySearchIndex, tierOptions }),
+ ...this.getCommonConfig(),
...this.getEntitySpecificQueryBuilderFields(entitySearchIndex),
};
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/QueryBuilderUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/QueryBuilderUtils.tsx
index afd1b669fc3..4ab55ea0ae5 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/QueryBuilderUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/QueryBuilderUtils.tsx
@@ -864,3 +864,18 @@ export const getEntityTypeAggregationFilter = (
return qFilter;
};
+
+export const getFieldsByKeys = (
+ keys: EntityReferenceFields[],
+ mapFields: Record
+): Record => {
+ const filteredFields: Record = {};
+
+ keys.forEach((key) => {
+ if (mapFields[key]) {
+ filteredFields[key] = mapFields[key];
+ }
+ });
+
+ return filteredFields;
+};