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 cc1f0df695b3..34fba24d6c80 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 86738d11dc52..c26a0064b373 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 0ea302cd0061..075361568eec 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 90651e429845..2aab18773b56 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 a572a7b4c3c5..b38d19a6ba8a 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 708d91ae94db..93834477741a 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 61baee49f856..a6b5db9165ec 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 3f9bc9ce950a..d5c554a45dcb 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/mocks/IngestionListTable.mock.ts b/openmetadata-ui/src/main/resources/ui/src/mocks/IngestionListTable.mock.ts
index e0ae76f1857f..9e3a3865f529 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 f9fee0938250..28f31633bc5e 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,134 @@ 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.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 05da5daaffd5..cc87cc631433 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchClassBase.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchClassBase.ts
@@ -143,6 +143,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
*/
@@ -477,7 +495,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 +516,7 @@ class AdvancedSearchClassBase {
mainWidgetProps: this.mainWidgetProps,
fieldSettings: {
asyncFetch: this.autocomplete({
- searchIndex: SearchIndex.DATA_ASSET,
+ searchIndex: entitySearchIndex,
entityField: EntityFields.NAME_KEYWORD,
}),
useAsyncSearch: true,
@@ -674,6 +692,7 @@ 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.ALL]: {
...this.tableQueryBuilderFields,
...this.pipelineQueryBuilderFields,
@@ -699,9 +718,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 9e50c736e007..86b84c97da2e 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}