From 6b230f3440bf447474e35df49bbd4efdde01c031 Mon Sep 17 00:00:00 2001 From: Aniket Katkar Date: Tue, 7 Jan 2025 08:57:15 +0530 Subject: [PATCH] Improvements: Automator requirements oss (#19128) * Fix the ingestion success screen text overflow issue * Fix the icon alignment issue in the in the entity header * Fix the permissions API calls for the ingestion pipelines to use fqn instead of names * Introduce the moreActionButtonProps in the PipelineActions component * Add the "Database" field in the advanced search for the "Database Schema" asset. Change the fields listing behavior in the advanced search modal, to show the common fields for the multiple assets instead of union of the fields. Change the search index for the "Name" and "Display Name" field suggestions to use the selected data assets instead of the all "Data Assets" index. * Remove the unnecessary ellipsis prop * Scan and add the missing filters for all the asset to have hierarchical parent filters. * Fix unit test * Fix the advanced search filter field search --- .../EntityHeaderTitle.component.tsx | 2 +- .../IngestionListTable.test.tsx | 38 ++++- .../IngestionListTable/IngestionListTable.tsx | 5 +- .../PipelineActions.interface.ts | 10 +- .../PipelineActions/PipelineActions.tsx | 4 +- .../PipelineActionsDropdown.interface.ts | 2 + .../PipelineActionsDropdown.test.tsx | 22 +++ .../PipelineActionsDropdown.tsx | 2 + .../common/SuccessScreen/SuccessScreen.tsx | 5 +- .../src/constants/AdvancedSearch.constants.ts | 6 + .../ui/src/enums/AdvancedSearch.enum.ts | 1 + .../ui/src/mocks/IngestionListTable.mock.ts | 2 +- .../src/utils/AdvancedSearchClassBase.test.ts | 137 +++++++++++++++++- .../ui/src/utils/AdvancedSearchClassBase.ts | 121 ++++++++++++++-- .../resources/ui/src/utils/IngestionUtils.tsx | 2 +- 15 files changed, 329 insertions(+), 30 deletions(-) rename openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/{ => IngestionListTable/PipelineActions}/PipelineActions.interface.ts (72%) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityHeaderTitle/EntityHeaderTitle.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityHeaderTitle/EntityHeaderTitle.component.tsx index cc1f0df695b..34fba24d6c8 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityHeaderTitle/EntityHeaderTitle.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityHeaderTitle/EntityHeaderTitle.component.tsx @@ -55,7 +55,7 @@ const EntityHeaderTitle = ({ data-testid={`${serviceName}-${name}`} gutter={12} wrap={false}> - {icon && {icon}} + {icon && {icon}} ({ useApplicationStore: jest.fn(() => ({ theme: { primaryColor: '#fff' }, @@ -260,4 +267,33 @@ describe('Ingestion', () => { expect(deleteIngestionPipelineById).toHaveBeenCalledWith('id'); }); + + it('should fetch the permissions for all the ingestion pipelines', async () => { + (usePermissionProvider as jest.Mock).mockImplementation(() => ({ + getEntityPermissionByFqn: mockGetEntityPermissionByFqn, + })); + + await act(async () => { + render( + , + { + wrapper: MemoryRouter, + } + ); + }); + + expect(mockGetEntityPermissionByFqn).toHaveBeenNthCalledWith( + 1, + 'ingestionPipeline', + mockESIngestionData.fullyQualifiedName + ); + expect(mockGetEntityPermissionByFqn).toHaveBeenNthCalledWith( + 2, + 'ingestionPipeline', + mockIngestionData.fullyQualifiedName + ); + }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/IngestionListTable.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/IngestionListTable.tsx index d576ed3b631..c0123163103 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/IngestionListTable.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/IngestionListTable.tsx @@ -157,7 +157,10 @@ function IngestionListTable({ const fetchIngestionPipelinesPermission = useCallback(async () => { try { const promises = ingestionData.map((item) => - getEntityPermissionByFqn(ResourceEntity.INGESTION_PIPELINE, item.name) + getEntityPermissionByFqn( + ResourceEntity.INGESTION_PIPELINE, + item.fullyQualifiedName ?? '' + ) ); const response = await Promise.allSettled(promises); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/PipelineActions.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/PipelineActions/PipelineActions.interface.ts similarity index 72% rename from openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/PipelineActions.interface.ts rename to openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/PipelineActions/PipelineActions.interface.ts index 0ea302cd006..075361568ee 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/PipelineActions.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/PipelineActions/PipelineActions.interface.ts @@ -11,10 +11,11 @@ * limitations under the License. */ -import { IngestionServicePermission } from '../../../../context/PermissionProvider/PermissionProvider.interface'; -import { ServiceCategory } from '../../../../enums/service.enum'; -import { IngestionPipeline } from '../../../../generated/entity/services/ingestionPipelines/ingestionPipeline'; -import { SelectedRowDetails } from './ingestion.interface'; +import { ButtonProps } from 'antd'; +import { IngestionServicePermission } from '../../../../../../context/PermissionProvider/PermissionProvider.interface'; +import { ServiceCategory } from '../../../../../../enums/service.enum'; +import { IngestionPipeline } from '../../../../../../generated/entity/services/ingestionPipelines/ingestionPipeline'; +import { SelectedRowDetails } from '../../ingestion.interface'; export interface PipelineActionsProps { pipeline: IngestionPipeline; @@ -28,4 +29,5 @@ export interface PipelineActionsProps { handleEnableDisableIngestion?: (id: string) => Promise; handleIsConfirmationModalOpen: (value: boolean) => void; onIngestionWorkflowsUpdate?: () => void; + moreActionButtonProps?: ButtonProps; } diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/PipelineActions/PipelineActions.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/PipelineActions/PipelineActions.tsx index 90651e42984..2aab18773b5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/PipelineActions/PipelineActions.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/PipelineActions/PipelineActions.tsx @@ -22,8 +22,8 @@ import { Operation } from '../../../../../../generated/entity/policies/accessCon import { PipelineType } from '../../../../../../generated/entity/services/ingestionPipelines/ingestionPipeline'; import { getLoadingStatus } from '../../../../../../utils/CommonUtils'; import { getLogsViewerPath } from '../../../../../../utils/RouterUtils'; -import { PipelineActionsProps } from '../../PipelineActions.interface'; import './pipeline-actions.less'; +import { PipelineActionsProps } from './PipelineActions.interface'; import PipelineActionsDropdown from './PipelineActionsDropdown'; function PipelineActions({ @@ -38,6 +38,7 @@ function PipelineActions({ handleIsConfirmationModalOpen, onIngestionWorkflowsUpdate, handleEditClick, + moreActionButtonProps, }: Readonly) { const history = useHistory(); const { t } = useTranslation(); @@ -168,6 +169,7 @@ function PipelineActions({ handleIsConfirmationModalOpen={handleIsConfirmationModalOpen} ingestion={pipeline} ingestionPipelinePermissions={ingestionPipelinePermissions} + moreActionButtonProps={moreActionButtonProps} serviceCategory={serviceCategory} serviceName={serviceName} triggerIngestion={triggerIngestion} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/PipelineActions/PipelineActionsDropdown.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/PipelineActions/PipelineActionsDropdown.interface.ts index a572a7b4c3c..b38d19a6ba8 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/PipelineActions/PipelineActionsDropdown.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/PipelineActions/PipelineActionsDropdown.interface.ts @@ -11,6 +11,7 @@ * limitations under the License. */ +import { ButtonProps } from 'antd'; import { IngestionServicePermission } from '../../../../../../context/PermissionProvider/PermissionProvider.interface'; import { IngestionPipeline } from '../../../../../../generated/entity/services/ingestionPipelines/ingestionPipeline'; import { SelectedRowDetails } from '../../ingestion.interface'; @@ -26,4 +27,5 @@ export interface PipelineActionsDropdownProps { handleDeleteSelection: (row: SelectedRowDetails) => void; handleIsConfirmationModalOpen: (value: boolean) => void; onIngestionWorkflowsUpdate?: () => void; + moreActionButtonProps?: ButtonProps; } diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/PipelineActions/PipelineActionsDropdown.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/PipelineActions/PipelineActionsDropdown.test.tsx index 708d91ae94d..93834477741 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/PipelineActions/PipelineActionsDropdown.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/PipelineActions/PipelineActionsDropdown.test.tsx @@ -324,4 +324,26 @@ describe('PipelineActionsDropdown', () => { expect(screen.queryByText('KillIngestionPipelineModal')).toBeNull(); }); + + it('should pass the moreActionButtonProps to the more action button', async () => { + const mockOnClick = jest.fn(); + + await act(async () => { + render( + , + { + wrapper: MemoryRouter, + } + ); + }); + + await clickOnMoreActions(); + + expect(mockOnClick).toHaveBeenCalled(); + }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/PipelineActions/PipelineActionsDropdown.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/PipelineActions/PipelineActionsDropdown.tsx index 61baee49f85..a6b5db9165e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/PipelineActions/PipelineActionsDropdown.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionListTable/PipelineActions/PipelineActionsDropdown.tsx @@ -50,6 +50,7 @@ function PipelineActionsDropdown({ handleIsConfirmationModalOpen, onIngestionWorkflowsUpdate, ingestionPipelinePermissions, + moreActionButtonProps, }: Readonly) { const history = useHistory(); const { t } = useTranslation(); @@ -270,6 +271,7 @@ function PipelineActionsDropdown({ icon={} type="link" onClick={() => setIsOpen((value) => !value)} + {...moreActionButtonProps} /> {isKillModalOpen && selectedPipeline && id === selectedPipeline?.id && ( diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/SuccessScreen/SuccessScreen.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/SuccessScreen/SuccessScreen.tsx index 3f9bc9ce950..d5c554a45dc 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/SuccessScreen/SuccessScreen.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/SuccessScreen/SuccessScreen.tsx @@ -101,10 +101,7 @@ const SuccessScreen = ({ - + {isUndefined(successMessage) ? ( diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/AdvancedSearch.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/AdvancedSearch.constants.ts index 6d93765a027..74a10290ad1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/AdvancedSearch.constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/AdvancedSearch.constants.ts @@ -351,3 +351,9 @@ export const EXPLORE_ROOT_INDEX_MAPPING = { ], Governance: [SearchIndex.GLOSSARY_TERM], }; +export const SEARCH_INDICES_WITH_COLUMNS_FIELD = [ + SearchIndex.TABLE, + SearchIndex.DASHBOARD_DATA_MODEL, + SearchIndex.DATA_ASSET, + SearchIndex.ALL, +]; 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 355f7d8894f..f138dad2994 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 @@ -62,6 +62,7 @@ export enum EntityFields { DATABASE_DISPLAY_NAME = 'database.displayName', DATABASE_SCHEMA_DISPLAY_NAME = 'databaseSchema.displayName', COLUMN = 'columns.name.keyword', + API_COLLECTION = 'apiCollection.displayName.keyword', CHART = 'charts.displayName.keyword', TASK = 'tasks.displayName.keyword', GLOSSARY_TERM_STATUS = 'status', diff --git a/openmetadata-ui/src/main/resources/ui/src/mocks/IngestionListTable.mock.ts b/openmetadata-ui/src/main/resources/ui/src/mocks/IngestionListTable.mock.ts index e0ae76f1857..9e3a3865f52 100644 --- a/openmetadata-ui/src/main/resources/ui/src/mocks/IngestionListTable.mock.ts +++ b/openmetadata-ui/src/main/resources/ui/src/mocks/IngestionListTable.mock.ts @@ -13,8 +13,8 @@ import { AddIngestionButtonProps } from '../components/Settings/Services/Ingestion/AddIngestionButton.interface'; import { IngestionListTableProps } from '../components/Settings/Services/Ingestion/IngestionListTable/IngestionListTable.interface'; +import { PipelineActionsProps } from '../components/Settings/Services/Ingestion/IngestionListTable/PipelineActions/PipelineActions.interface'; import { PipelineActionsDropdownProps } from '../components/Settings/Services/Ingestion/IngestionListTable/PipelineActions/PipelineActionsDropdown.interface'; -import { PipelineActionsProps } from '../components/Settings/Services/Ingestion/PipelineActions.interface'; import { ServiceCategory } from '../enums/service.enum'; import { DatabaseServiceType } from '../generated/entity/data/database'; import { ConfigType } from '../generated/entity/services/databaseService'; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchClassBase.test.ts b/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchClassBase.test.ts index f9fee093825..537fdc377d6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchClassBase.test.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchClassBase.test.ts @@ -11,7 +11,10 @@ * limitations under the License. */ import { EntityFields } from '../enums/AdvancedSearch.enum'; -import { AdvancedSearchClassBase } from './AdvancedSearchClassBase'; +import { SearchIndex } from '../enums/search.enum'; +import advancedSearchClassBase, { + AdvancedSearchClassBase, +} from './AdvancedSearchClassBase'; jest.mock('../rest/miscAPI', () => ({ getAggregateFieldOptions: jest.fn().mockImplementation(() => @@ -50,3 +53,135 @@ describe('AdvancedSearchClassBase', () => { ]); }); }); + +describe('getEntitySpecificQueryBuilderFields', () => { + it('should return table specific fields', () => { + const result = advancedSearchClassBase.getEntitySpecificQueryBuilderFields([ + SearchIndex.TABLE, + ]); + + expect(Object.keys(result)).toEqual([ + EntityFields.DATABASE, + EntityFields.DATABASE_SCHEMA, + EntityFields.TABLE_TYPE, + EntityFields.COLUMN_DESCRIPTION_STATUS, + ]); + }); + + it('should return pipeline specific fields', () => { + const result = advancedSearchClassBase.getEntitySpecificQueryBuilderFields([ + SearchIndex.PIPELINE, + ]); + + expect(Object.keys(result)).toEqual([EntityFields.TASK]); + }); + + it('should return dashboard specific fields', () => { + const result = advancedSearchClassBase.getEntitySpecificQueryBuilderFields([ + SearchIndex.DASHBOARD, + ]); + + expect(Object.keys(result)).toEqual([ + EntityFields.DATA_MODEL, + EntityFields.CHART, + EntityFields.PROJECT, + ]); + }); + + it('should return topic specific fields', () => { + const result = advancedSearchClassBase.getEntitySpecificQueryBuilderFields([ + SearchIndex.TOPIC, + ]); + + expect(Object.keys(result)).toEqual([EntityFields.SCHEMA_FIELD]); + }); + + it('should return mlModel specific fields', () => { + const result = advancedSearchClassBase.getEntitySpecificQueryBuilderFields([ + SearchIndex.MLMODEL, + ]); + + expect(Object.keys(result)).toEqual([EntityFields.FEATURE]); + }); + + it('should return container specific fields', () => { + const result = advancedSearchClassBase.getEntitySpecificQueryBuilderFields([ + SearchIndex.CONTAINER, + ]); + + expect(Object.keys(result)).toEqual([EntityFields.CONTAINER_COLUMN]); + }); + + it('should return searchIndex specific fields', () => { + const result = advancedSearchClassBase.getEntitySpecificQueryBuilderFields([ + SearchIndex.SEARCH_INDEX, + ]); + + expect(Object.keys(result)).toEqual([EntityFields.FIELD]); + }); + + it('should return dataModel specific fields', () => { + const result = advancedSearchClassBase.getEntitySpecificQueryBuilderFields([ + SearchIndex.DASHBOARD_DATA_MODEL, + ]); + + expect(Object.keys(result)).toEqual([ + EntityFields.DATA_MODEL_TYPE, + EntityFields.PROJECT, + ]); + }); + + it('should return apiEndpoint specific fields', () => { + const result = advancedSearchClassBase.getEntitySpecificQueryBuilderFields([ + SearchIndex.API_ENDPOINT_INDEX, + ]); + + expect(Object.keys(result)).toEqual([ + EntityFields.API_COLLECTION, + EntityFields.REQUEST_SCHEMA_FIELD, + EntityFields.RESPONSE_SCHEMA_FIELD, + ]); + }); + + it('should return glossary specific fields', () => { + const result = advancedSearchClassBase.getEntitySpecificQueryBuilderFields([ + SearchIndex.GLOSSARY_TERM, + ]); + + expect(Object.keys(result)).toEqual([EntityFields.GLOSSARY_TERM_STATUS]); + }); + + it('should return databaseSchema specific fields', () => { + const result = advancedSearchClassBase.getEntitySpecificQueryBuilderFields([ + SearchIndex.DATABASE_SCHEMA, + ]); + + expect(Object.keys(result)).toEqual([EntityFields.DATABASE]); + }); + + it('should return empty fields for multiple indices with no common fields', () => { + const result = advancedSearchClassBase.getEntitySpecificQueryBuilderFields([ + SearchIndex.TABLE, + SearchIndex.PIPELINE, + ]); + + expect(Object.keys(result)).toEqual([]); + }); + + it('should return combined fields for multiple indices with common fields', () => { + const result = advancedSearchClassBase.getEntitySpecificQueryBuilderFields([ + SearchIndex.TABLE, + SearchIndex.DATABASE_SCHEMA, + ]); + + expect(Object.keys(result)).toEqual([EntityFields.DATABASE]); + }); + + it('should return empty object for unknown index', () => { + const result = advancedSearchClassBase.getEntitySpecificQueryBuilderFields([ + 'UNKNOWN_INDEX' as SearchIndex, + ]); + + expect(Object.keys(result)).toEqual([]); + }); +}); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchClassBase.ts b/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchClassBase.ts index 05da5daaffd..12ed537d1c2 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchClassBase.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchClassBase.ts @@ -22,6 +22,7 @@ import { SelectFieldSettings, } from 'react-awesome-query-builder'; import AntdConfig from 'react-awesome-query-builder/lib/config/antd'; +import { SEARCH_INDICES_WITH_COLUMNS_FIELD } from '../constants/AdvancedSearch.constants'; import { EntityFields, SuggestionField } from '../enums/AdvancedSearch.enum'; import { SearchIndex } from '../enums/search.enum'; import { getAggregateFieldOptions } from '../rest/miscAPI'; @@ -143,6 +144,24 @@ class AdvancedSearchClassBase { }; }; + /** + * Fields specific to database schema + */ + databaseSchemaQueryBuilderFields: Fields = { + [EntityFields.DATABASE]: { + label: t('label.database'), + type: 'select', + mainWidgetProps: this.mainWidgetProps, + fieldSettings: { + asyncFetch: this.autocomplete({ + searchIndex: SearchIndex.DATABASE_SCHEMA, + entityField: EntityFields.DATABASE, + }), + useAsyncSearch: true, + }, + }, + }; + /** * Fields specific to tables */ @@ -206,6 +225,37 @@ class AdvancedSearchClassBase { }, }; + /** + * Fields specific to stored procedures + */ + storedProcedureQueryBuilderFields: Fields = { + [EntityFields.DATABASE]: { + label: t('label.database'), + type: 'select', + mainWidgetProps: this.mainWidgetProps, + fieldSettings: { + asyncFetch: this.autocomplete({ + searchIndex: SearchIndex.STORED_PROCEDURE, + entityField: EntityFields.DATABASE, + }), + useAsyncSearch: true, + }, + }, + + [EntityFields.DATABASE_SCHEMA]: { + label: t('label.database-schema'), + type: 'select', + mainWidgetProps: this.mainWidgetProps, + fieldSettings: { + asyncFetch: this.autocomplete({ + searchIndex: SearchIndex.STORED_PROCEDURE, + entityField: EntityFields.DATABASE_SCHEMA, + }), + useAsyncSearch: true, + }, + }, + }; + /** * Fields specific to pipelines */ @@ -246,6 +296,18 @@ class AdvancedSearchClassBase { * Fields specific to API endpoints */ apiEndpointQueryBuilderFields: Fields = { + [EntityFields.API_COLLECTION]: { + label: t('label.api-collection'), + type: 'select', + mainWidgetProps: this.mainWidgetProps, + fieldSettings: { + asyncFetch: this.autocomplete({ + searchIndex: SearchIndex.API_ENDPOINT_INDEX, + entityField: EntityFields.API_COLLECTION, + }), + useAsyncSearch: true, + }, + }, [EntityFields.REQUEST_SCHEMA_FIELD]: { label: t('label.request-schema-field'), type: 'select', @@ -438,6 +500,17 @@ class AdvancedSearchClassBase { renderButton: isExplorePage ? renderAdvanceSearchButtons : renderQueryBuilderFilterButtons, + + customFieldSelectProps: { + ...this.baseConfig.settings.customFieldSelectProps, + // Adding filterOption to search by label + // Since the default search behavior is by value which gives incorrect results + // Ex. for search term 'name', it will return 'Task' in results as well + // since value for 'Task' is 'tasks.displayName.keyword' + filterOption: (input: string, option: { label: string }) => { + return option.label.toLowerCase().includes(input.toLowerCase()); + }, + }, }, }; @@ -477,7 +550,7 @@ class AdvancedSearchClassBase { mainWidgetProps: this.mainWidgetProps, fieldSettings: { asyncFetch: this.autocomplete({ - searchIndex: SearchIndex.DATA_ASSET, + searchIndex: entitySearchIndex, entityField: EntityFields.DISPLAY_NAME_KEYWORD, }), useAsyncSearch: true, @@ -498,7 +571,7 @@ class AdvancedSearchClassBase { mainWidgetProps: this.mainWidgetProps, fieldSettings: { asyncFetch: this.autocomplete({ - searchIndex: SearchIndex.DATA_ASSET, + searchIndex: entitySearchIndex, entityField: EntityFields.NAME_KEYWORD, }), useAsyncSearch: true, @@ -627,18 +700,14 @@ class AdvancedSearchClassBase { } // Since the column field key 'columns.name.keyword` is common in table and data model, - // Following function is used to get the column field config based on the search index - // or if it is an explore page + // Following function is used to get the column field config if all the search Indices have columns field + // or for ALL and DATA_ASSET search indices public getColumnConfig = (entitySearchIndex: SearchIndex[]) => { - const searchIndexWithColumns = entitySearchIndex.filter( - (index) => - index === SearchIndex.TABLE || - index === SearchIndex.DASHBOARD_DATA_MODEL || - index === SearchIndex.DATA_ASSET || - index === SearchIndex.ALL + const shouldAddColumnField = entitySearchIndex.every((index) => + SEARCH_INDICES_WITH_COLUMNS_FIELD.includes(index) ); - return !isEmpty(searchIndexWithColumns) + return shouldAddColumnField ? { [EntityFields.COLUMN]: { label: t('label.column'), @@ -646,7 +715,7 @@ class AdvancedSearchClassBase { mainWidgetProps: this.mainWidgetProps, fieldSettings: { asyncFetch: this.autocomplete({ - searchIndex: searchIndexWithColumns, + searchIndex: entitySearchIndex, entityField: EntityFields.COLUMN, }), useAsyncSearch: true, @@ -674,6 +743,8 @@ class AdvancedSearchClassBase { [SearchIndex.DASHBOARD_DATA_MODEL]: this.dataModelQueryBuilderFields, [SearchIndex.API_ENDPOINT_INDEX]: this.apiEndpointQueryBuilderFields, [SearchIndex.GLOSSARY_TERM]: this.glossaryQueryBuilderFields, + [SearchIndex.DATABASE_SCHEMA]: this.databaseSchemaQueryBuilderFields, + [SearchIndex.STORED_PROCEDURE]: this.storedProcedureQueryBuilderFields, [SearchIndex.ALL]: { ...this.tableQueryBuilderFields, ...this.pipelineQueryBuilderFields, @@ -699,9 +770,29 @@ class AdvancedSearchClassBase { }, }; - entitySearchIndex.forEach((index) => { - configs = { ...configs, ...(configIndexMapping[index] ?? {}) }; - }); + // Find out the common fields between the selected indices + if (!isEmpty(entitySearchIndex)) { + const firstIndex = entitySearchIndex[0]; + + // Fields config for the first index + configs = { ...configIndexMapping[firstIndex] }; + + // Iterate over the rest of the indices to see the common fields + entitySearchIndex.slice(1).forEach((index) => { + // Get the current config for the current iteration index + const currentConfig = configIndexMapping[index] ?? {}; + + // Filter out the fields that are not common between the current and previous configs + configs = Object.keys(configs).reduce((acc, key) => { + // If the key exists in the current config, add it to the accumulator + if (currentConfig[key]) { + acc[key] = configs[key]; + } + + return acc; + }, {} as Fields); + }); + } return configs; } diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/IngestionUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/IngestionUtils.tsx index 9e50c736e00..86b84c97da2 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/IngestionUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/IngestionUtils.tsx @@ -359,7 +359,7 @@ export const getSuccessMessage = ( return ( - {`"${ingestionName}"`} + {`"${ingestionName}"`} {status === FormSubmitType.ADD ? createMessage : updateMessage}