From fc58893239880c2048d1710639f699ce4ae1db6d Mon Sep 17 00:00:00 2001 From: Aniket Katkar Date: Fri, 11 Apr 2025 10:41:11 +0530 Subject: [PATCH] Chore(ui): AutoPilot status banner behaviour improvements (#20758) * Modify the AutoPilot status banner visibility banner to hide once closed until and unless the status changes again * Add test case for asyncDeleteProvider * Fix the sonarcloud issues * Fix the failing playwright tests * Fix the playwright for delete service --- .../ui/playwright/utils/serviceIngestion.ts | 16 +++++++- .../APIEndpointDetails/APIEndpointDetails.tsx | 3 +- .../DashboardDetails.component.tsx | 5 +-- .../DataModels/DataModelDetails.component.tsx | 3 +- .../Metric/MetricDetails/MetricDetails.tsx | 3 +- .../MlModelDetail/MlModelDetail.component.tsx | 5 +-- .../PipelineDetails.component.tsx | 5 +-- .../ServiceInsights/ServiceInsightsTab.tsx | 38 ++++++++++++++++++- .../TopicDetails/TopicDetails.component.tsx | 3 +- .../common/DeleteWidget/DeleteWidgetModal.tsx | 2 + .../src/constants/LocalStorage.constants.ts | 14 +++++++ .../AsyncDeleteProvider.interface.ts | 1 + .../AsyncDeleteProvider.test.tsx | 17 +++++++++ .../AsyncDeleteProvider.tsx | 4 ++ .../APICollectionPage/APICollectionPage.tsx | 3 +- .../src/pages/ContainerPage/ContainerPage.tsx | 5 +-- .../DatabaseDetailsPage.tsx | 5 +-- .../DatabaseSchemaPage.component.tsx | 3 +- .../SearchIndexDetailsPage.tsx | 5 +-- .../ServiceDetailsPage/ServiceDetailsPage.tsx | 30 +++++++++------ .../StoredProcedure/StoredProcedurePage.tsx | 7 ++-- .../TableDetailsPageV1/TableDetailsPageV1.tsx | 11 +++--- .../src/utils/LocalStorageUtils.interface.ts | 18 +++++++++ .../ui/src/utils/LocalStorageUtils.ts | 37 ++++++++++++++++++ .../ui/src/utils/ServiceInsightsTabUtils.tsx | 17 +++++++++ 25 files changed, 206 insertions(+), 54 deletions(-) create mode 100644 openmetadata-ui/src/main/resources/ui/src/constants/LocalStorage.constants.ts create mode 100644 openmetadata-ui/src/main/resources/ui/src/utils/LocalStorageUtils.interface.ts diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/serviceIngestion.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/serviceIngestion.ts index 2aa42721829..a65fc814c2b 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/serviceIngestion.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/serviceIngestion.ts @@ -122,8 +122,22 @@ export const deleteService = async ( // Closing the toast notification await toastNotification(page, /deleted successfully!/, 5 * 60 * 1000); // Wait for up to 5 minutes for the toast notification to appear + await page.reload(); + await page.waitForLoadState('networkidle'); + await page.waitForSelector('[data-testid="loader"]', { state: 'detached' }); + + const serviceSearchResponse = page.waitForResponse( + `/api/v1/search/query?q=*${encodeURIComponent( + escapeESReservedCharacters(serviceName) + )}*` + ); + + await page.fill('[data-testid="searchbar"]', serviceName); + + await serviceSearchResponse; + await page.waitForSelector(`[data-testid="service-name-${serviceName}"]`, { - state: 'hidden', + state: 'detached', }); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/APIEndpoint/APIEndpointDetails/APIEndpointDetails.tsx b/openmetadata-ui/src/main/resources/ui/src/components/APIEndpoint/APIEndpointDetails/APIEndpointDetails.tsx index e2cb9c82a79..68f860441fd 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/APIEndpoint/APIEndpointDetails/APIEndpointDetails.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/APIEndpoint/APIEndpointDetails/APIEndpointDetails.tsx @@ -172,8 +172,7 @@ const APIEndpointDetails: React.FC = ({ ); const afterDeleteAction = useCallback( - (isSoftDelete?: boolean, version?: number) => - isSoftDelete ? onToggleDelete(version) : history.push('/'), + (isSoftDelete?: boolean) => !isSoftDelete && history.push('/'), [] ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DashboardDetails/DashboardDetails.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DashboardDetails/DashboardDetails.component.tsx index 98ee6f74243..475af31d9fa 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DashboardDetails/DashboardDetails.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DashboardDetails/DashboardDetails.component.tsx @@ -102,7 +102,7 @@ const DashboardDetails = ({ dashboardDetails.id ); setDashboardPermissions(entityPermission); - } catch (error) { + } catch { showErrorToast( t('server.fetch-entity-permissions-error', { entity: t('label.dashboard'), @@ -203,8 +203,7 @@ const DashboardDetails = ({ }; const afterDeleteAction = useCallback( - (isSoftDelete?: boolean, version?: number) => - isSoftDelete ? handleToggleDelete(version) : history.push('/'), + (isSoftDelete?: boolean) => !isSoftDelete && history.push('/'), [] ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DataModel/DataModels/DataModelDetails.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DataModel/DataModels/DataModelDetails.component.tsx index 8d5295c6b3d..54a05c3fe6a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DataModel/DataModels/DataModelDetails.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Dashboard/DataModel/DataModels/DataModelDetails.component.tsx @@ -151,8 +151,7 @@ const DataModelDetails = ({ }; const afterDeleteAction = useCallback( - (isSoftDelete?: boolean, version?: number) => - isSoftDelete ? handleToggleDelete(version) : history.push('/'), + (isSoftDelete?: boolean) => !isSoftDelete && history.push('/'), [] ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Metric/MetricDetails/MetricDetails.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Metric/MetricDetails/MetricDetails.tsx index 29d12e10b98..8bc9397deba 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Metric/MetricDetails/MetricDetails.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Metric/MetricDetails/MetricDetails.tsx @@ -155,8 +155,7 @@ const MetricDetails: React.FC = ({ getFeedCounts(EntityType.METRIC, decodedMetricFqn, handleFeedCount); const afterDeleteAction = useCallback( - (isSoftDelete?: boolean, version?: number) => - isSoftDelete ? onToggleDelete(version) : history.push(ROUTES.METRICS), + (isSoftDelete?: boolean) => !isSoftDelete && history.push(ROUTES.METRICS), [] ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MlModel/MlModelDetail/MlModelDetail.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MlModel/MlModelDetail/MlModelDetail.component.tsx index 42f614271fa..ea16b62c895 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/MlModel/MlModelDetail/MlModelDetail.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/MlModel/MlModelDetail/MlModelDetail.component.tsx @@ -97,7 +97,7 @@ const MlModelDetail: FC = ({ mlModelDetail.id ); setMlModelPermissions(entityPermission); - } catch (error) { + } catch { showErrorToast( t('server.fetch-entity-permissions-error', { entity: t('label.ml-model'), @@ -294,8 +294,7 @@ const MlModelDetail: FC = ({ }, [mlModelDetail, mlModelStoreColumn]); const afterDeleteAction = useCallback( - (isSoftDelete?: boolean, version?: number) => - isSoftDelete ? handleToggleDelete(version) : history.push('/'), + (isSoftDelete?: boolean) => !isSoftDelete && history.push('/'), [] ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Pipeline/PipelineDetails/PipelineDetails.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Pipeline/PipelineDetails/PipelineDetails.component.tsx index bf741c56de6..1d4732facc1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Pipeline/PipelineDetails/PipelineDetails.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Pipeline/PipelineDetails/PipelineDetails.component.tsx @@ -115,7 +115,7 @@ const PipelineDetails = ({ pipelineDetails.id ); setPipelinePermissions(entityPermission); - } catch (error) { + } catch { showErrorToast( t('server.fetch-entity-permissions-error', { entity: t('label.asset-lowercase'), @@ -254,8 +254,7 @@ const PipelineDetails = ({ }; const afterDeleteAction = useCallback( - (isSoftDelete?: boolean, version?: number) => - isSoftDelete ? handleToggleDelete(version) : history.push('/'), + (isSoftDelete?: boolean) => !isSoftDelete && history.push('/'), [] ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/ServiceInsights/ServiceInsightsTab.tsx b/openmetadata-ui/src/main/resources/ui/src/components/ServiceInsights/ServiceInsightsTab.tsx index bfcd4e22ae1..44da9b1dcff 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/ServiceInsights/ServiceInsightsTab.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/ServiceInsights/ServiceInsightsTab.tsx @@ -17,7 +17,7 @@ import { AxiosError } from 'axios'; import classNames from 'classnames'; import { isUndefined } from 'lodash'; import { ServiceTypes } from 'Models'; -import React, { useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useParams } from 'react-router-dom'; import { PLATFORM_INSIGHTS_CHART } from '../../constants/ServiceInsightsTab.constants'; import { SystemChartType } from '../../enums/DataInsight.enum'; @@ -27,7 +27,9 @@ import { getCurrentDayStartGMTinMillis, getDayAgoStartGMTinMillis, } from '../../utils/date-time/DateTimeUtils'; +import { updateAutoPilotStatus } from '../../utils/LocalStorageUtils'; import { + checkIfAutoPilotStatusIsDismissed, filterDistributionChartItem, getPlatformInsightsChartDataFormattingMethod, getStatusIconFromStatusType, @@ -144,9 +146,40 @@ const ServiceInsightsTab = ({ workflowStatesData?.mainInstanceState?.status ); + const showAutoPilotStatus = useMemo(() => { + const isDataPresent = + !isWorkflowStatusLoading && !isUndefined(workflowStatesData); + const isStatusDismissed = checkIfAutoPilotStatusIsDismissed( + serviceDetails.fullyQualifiedName, + workflowStatesData?.mainInstanceState?.status + ); + + return isDataPresent && !isStatusDismissed; + }, [ + isWorkflowStatusLoading, + workflowStatesData, + serviceDetails.fullyQualifiedName, + workflowStatesData?.mainInstanceState?.status, + ]); + + const onStatusBannerClose = useCallback(() => { + if ( + serviceDetails.fullyQualifiedName && + workflowStatesData?.mainInstanceState?.status + ) { + updateAutoPilotStatus({ + serviceFQN: serviceDetails.fullyQualifiedName, + status: workflowStatesData?.mainInstanceState?.status, + }); + } + }, [ + serviceDetails.fullyQualifiedName, + workflowStatesData?.mainInstanceState?.status, + ]); + return ( - {!isWorkflowStatusLoading && !isUndefined(workflowStatesData) && ( + {showAutoPilotStatus && ( } message={message} + onClose={onStatusBannerClose} /> )} {arrayOfWidgets.map( diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Topic/TopicDetails/TopicDetails.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Topic/TopicDetails/TopicDetails.component.tsx index a96d52550a0..a76ec38e81b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Topic/TopicDetails/TopicDetails.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Topic/TopicDetails/TopicDetails.component.tsx @@ -241,8 +241,7 @@ const TopicDetails: React.FC = ({ getFeedCounts(EntityType.TOPIC, decodedTopicFQN, handleFeedCount); const afterDeleteAction = useCallback( - (isSoftDelete?: boolean, version?: number) => - isSoftDelete ? handleToggleDelete(version) : history.push('/'), + (isSoftDelete?: boolean) => !isSoftDelete && history.push('/'), [] ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/DeleteWidget/DeleteWidgetModal.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/DeleteWidget/DeleteWidgetModal.tsx index c2da4435387..1b79d0dbdfe 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/DeleteWidget/DeleteWidgetModal.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/DeleteWidget/DeleteWidgetModal.tsx @@ -218,6 +218,7 @@ const DeleteWidgetModal = ({ deleteType: values.deleteType, prepareType, isRecursiveDelete: isRecursiveDelete ?? false, + afterDeleteAction, }); setIsLoading(false); handleOnEntityDeleteCancel(); @@ -234,6 +235,7 @@ const DeleteWidgetModal = ({ isRecursiveDelete, handleOnEntityDeleteConfirm, handleOnEntityDeleteCancel, + afterDeleteAction, ] ); diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/LocalStorage.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/LocalStorage.constants.ts new file mode 100644 index 00000000000..eff82940f5a --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/constants/LocalStorage.constants.ts @@ -0,0 +1,14 @@ +/* + * 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. + */ +export const LOCAL_STORAGE_AUTO_PILOT_STATUS = + 'serviceAutoPilotDismissedStatuses'; diff --git a/openmetadata-ui/src/main/resources/ui/src/context/AsyncDeleteProvider/AsyncDeleteProvider.interface.ts b/openmetadata-ui/src/main/resources/ui/src/context/AsyncDeleteProvider/AsyncDeleteProvider.interface.ts index 50092298a3a..8303f1016b2 100644 --- a/openmetadata-ui/src/main/resources/ui/src/context/AsyncDeleteProvider/AsyncDeleteProvider.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/context/AsyncDeleteProvider/AsyncDeleteProvider.interface.ts @@ -24,6 +24,7 @@ export interface DeleteWidgetAsyncFormFields { deleteType: DeleteType; prepareType: boolean; isRecursiveDelete: boolean; + afterDeleteAction?: (isSoftDelete?: boolean, version?: number) => void; } export interface AsyncDeleteContextType { diff --git a/openmetadata-ui/src/main/resources/ui/src/context/AsyncDeleteProvider/AsyncDeleteProvider.test.tsx b/openmetadata-ui/src/main/resources/ui/src/context/AsyncDeleteProvider/AsyncDeleteProvider.test.tsx index eaf31a9a98a..6d337c8d8ec 100644 --- a/openmetadata-ui/src/main/resources/ui/src/context/AsyncDeleteProvider/AsyncDeleteProvider.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/context/AsyncDeleteProvider/AsyncDeleteProvider.test.tsx @@ -30,6 +30,8 @@ jest.mock('../../rest/miscAPI', () => ({ deleteAsyncEntity: jest.fn().mockImplementation(() => Promise.resolve()), })); +const mockAfterDeleteAction = jest.fn(); + describe('AsyncDeleteProvider', () => { const mockResponse = { entityName: 'DELETE', @@ -95,6 +97,7 @@ describe('AsyncDeleteProvider', () => { mockError, 'server.delete-entity-error' ); + expect(mockAfterDeleteAction).not.toHaveBeenCalled(); }); it('should handle websocket response', async () => { @@ -158,4 +161,18 @@ describe('AsyncDeleteProvider', () => { false ); }); + + it('should execute afterDeleteAction if present', async () => { + (deleteAsyncEntity as jest.Mock).mockResolvedValueOnce(mockResponse); + const { result } = renderHook(() => useAsyncDeleteProvider(), { wrapper }); + + await act(async () => { + await result.current.handleOnAsyncEntityDeleteConfirm({ + ...mockDeleteParams, + afterDeleteAction: mockAfterDeleteAction, + }); + }); + + expect(mockAfterDeleteAction).toHaveBeenCalledWith(true); + }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/context/AsyncDeleteProvider/AsyncDeleteProvider.tsx b/openmetadata-ui/src/main/resources/ui/src/context/AsyncDeleteProvider/AsyncDeleteProvider.tsx index d88cbdde141..a8936ef4c17 100644 --- a/openmetadata-ui/src/main/resources/ui/src/context/AsyncDeleteProvider/AsyncDeleteProvider.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/context/AsyncDeleteProvider/AsyncDeleteProvider.tsx @@ -46,6 +46,7 @@ const AsyncDeleteProvider = ({ children }: AsyncDeleteProviderProps) => { deleteType, prepareType, isRecursiveDelete, + afterDeleteAction, }: DeleteWidgetAsyncFormFields) => { try { const response = await deleteAsyncEntity( @@ -73,6 +74,9 @@ const AsyncDeleteProvider = ({ children }: AsyncDeleteProviderProps) => { setAsyncDeleteJob(response); asyncDeleteJobRef.current = response; showSuccessToast(response.message); + if (afterDeleteAction) { + afterDeleteAction(deleteType === DeleteType.SOFT_DELETE); + } } catch (error) { showErrorToast( error as AxiosError, diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/APICollectionPage/APICollectionPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/APICollectionPage/APICollectionPage.tsx index 953e9014a70..63d3629823d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/APICollectionPage/APICollectionPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/APICollectionPage/APICollectionPage.tsx @@ -329,8 +329,7 @@ const APICollectionPage: FunctionComponent = () => { }, [currentVersion, decodedAPICollectionFQN]); const afterDeleteAction = useCallback( - (isSoftDelete?: boolean, version?: number) => - isSoftDelete ? handleToggleDelete(version) : history.push('/'), + (isSoftDelete?: boolean) => !isSoftDelete && history.push('/'), [] ); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/ContainerPage/ContainerPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/ContainerPage/ContainerPage.tsx index 7a7980c4fcc..30646e3c41c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/ContainerPage/ContainerPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/ContainerPage/ContainerPage.tsx @@ -160,7 +160,7 @@ const ContainerPage = () => { await fetchContainerDetail(containerFQN); getEntityFeedCount(); } - } catch (error) { + } catch { showErrorToast( t('server.fetch-entity-permissions-error', { entity: t('label.asset-lowercase'), @@ -364,8 +364,7 @@ const ContainerPage = () => { }; const afterDeleteAction = useCallback( - (isSoftDelete?: boolean, version?: number) => - isSoftDelete ? handleToggleDelete(version) : history.push('/'), + (isSoftDelete?: boolean) => !isSoftDelete && history.push('/'), [] ); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseDetailsPage/DatabaseDetailsPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseDetailsPage/DatabaseDetailsPage.tsx index 751ccf831c6..ff94b1df6d0 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseDetailsPage/DatabaseDetailsPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseDetailsPage/DatabaseDetailsPage.tsx @@ -139,7 +139,7 @@ const DatabaseDetails: FunctionComponent = () => { decodedDatabaseFQN ); setDatabasePermission(response); - } catch (error) { + } catch { // Error } finally { setIsLoading(false); @@ -368,8 +368,7 @@ const DatabaseDetails: FunctionComponent = () => { ); const afterDeleteAction = useCallback( - (isSoftDelete?: boolean, version?: number) => - isSoftDelete ? handleToggleDelete(version) : history.push('/'), + (isSoftDelete?: boolean) => !isSoftDelete && history.push('/'), [] ); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.component.tsx index 5861964f624..399da76cb14 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.component.tsx @@ -332,8 +332,7 @@ const DatabaseSchemaPage: FunctionComponent = () => { }, [currentVersion, decodedDatabaseSchemaFQN]); const afterDeleteAction = useCallback( - (isSoftDelete?: boolean, version?: number) => - isSoftDelete ? handleToggleDelete(version) : history.push('/'), + (isSoftDelete?: boolean) => !isSoftDelete && history.push('/'), [] ); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/SearchIndexDetailsPage/SearchIndexDetailsPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/SearchIndexDetailsPage/SearchIndexDetailsPage.tsx index ef25ae28a7a..794aec28924 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/SearchIndexDetailsPage/SearchIndexDetailsPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/SearchIndexDetailsPage/SearchIndexDetailsPage.tsx @@ -108,7 +108,7 @@ function SearchIndexDetailsPage() { timestamp: 0, id: details.id, }); - } catch (error) { + } catch { // Error here } finally { setLoading(false); @@ -482,8 +482,7 @@ function SearchIndexDetailsPage() { }, [version]); const afterDeleteAction = useCallback( - (isSoftDelete?: boolean, version?: number) => - isSoftDelete ? handleToggleDelete(version) : history.push('/'), + (isSoftDelete?: boolean) => !isSoftDelete && history.push('/'), [] ); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/ServiceDetailsPage/ServiceDetailsPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/ServiceDetailsPage/ServiceDetailsPage.tsx index 037a5811561..f2d126ff956 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/ServiceDetailsPage/ServiceDetailsPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/ServiceDetailsPage/ServiceDetailsPage.tsx @@ -106,6 +106,7 @@ import { } from '../../utils/date-time/DateTimeUtils'; import entityUtilClassBase from '../../utils/EntityUtilClassBase'; import { getEntityFeedLink, getEntityName } from '../../utils/EntityUtils'; +import { removeAutoPilotStatus } from '../../utils/LocalStorageUtils'; import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils'; import { getEditConnectionPath, @@ -961,16 +962,18 @@ const ServiceDetailsPage: FunctionComponent = () => { }, []); const afterDeleteAction = useCallback( - (isSoftDelete?: boolean, version?: number) => - isSoftDelete - ? handleToggleDelete(version) - : history.push( - getSettingPath( - GlobalSettingsMenuCategory.SERVICES, - getServiceRouteFromServiceType(serviceCategory) - ) - ), - [handleToggleDelete, serviceCategory] + (isSoftDelete?: boolean) => { + if (!isSoftDelete) { + removeAutoPilotStatus(serviceDetails.fullyQualifiedName ?? ''); + history.push( + getSettingPath( + GlobalSettingsMenuCategory.SERVICES, + getServiceRouteFromServiceType(serviceCategory) + ) + ); + } + }, + [serviceCategory, serviceDetails.fullyQualifiedName] ); const handleRestoreService = useCallback(async () => { @@ -1342,6 +1345,11 @@ const ServiceDetailsPage: FunctionComponent = () => { isWorkflowStatusLoading, ]); + const afterAutoPilotAppTrigger = useCallback(() => { + removeAutoPilotStatus(serviceDetails.fullyQualifiedName ?? ''); + fetchWorkflowInstanceStates(); + }, [serviceDetails.fullyQualifiedName, fetchWorkflowInstanceStates]); + if (isLoading) { return ; } @@ -1367,7 +1375,7 @@ const ServiceDetailsPage: FunctionComponent = () => { isRecursiveDelete afterDeleteAction={afterDeleteAction} afterDomainUpdateAction={afterDomainUpdateAction} - afterTriggerAction={fetchWorkflowInstanceStates} + afterTriggerAction={afterAutoPilotAppTrigger} dataAsset={serviceDetails} disableRunAgentsButton={disableRunAgentsButton} entityType={entityType} diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedurePage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedurePage.tsx index 899556457c2..b686a36a0d4 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedurePage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedurePage.tsx @@ -130,7 +130,7 @@ const StoredProcedurePage = () => { ); setStoredProcedurePermissions(permission); - } catch (error) { + } catch { showErrorToast( t('server.fetch-entity-permissions-error', { entity: t('label.resource-permission-lowercase'), @@ -358,8 +358,7 @@ const StoredProcedurePage = () => { ); const afterDeleteAction = useCallback( - (isSoftDelete?: boolean, version?: number) => - isSoftDelete ? handleToggleDelete(version) : history.push('/'), + (isSoftDelete?: boolean) => !isSoftDelete && history.push('/'), [] ); @@ -445,7 +444,7 @@ const StoredProcedurePage = () => { const tabLabelMap = getTabLabelMapFromTabs(customizedPage?.tabs); const tabs = getStoredProcedureDetailsPageTabs({ - activeTab: activeTab as EntityTabs, + activeTab, feedCount, decodedStoredProcedureFQN, entityName, diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.tsx index 5fd16f38bd8..be3dfb5687d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.tsx @@ -228,7 +228,7 @@ const TableDetailsPageV1: React.FC = () => { data.nodes?.filter((node) => node?.fullyQualifiedName !== tableFqn) ?? []; setDqFailureCount(updatedNodes.length); - } catch (error) { + } catch { setDqFailureCount(0); } }; @@ -259,7 +259,7 @@ const TableDetailsPageV1: React.FC = () => { } else { setDqFailureCount(failureCount); } - } catch (error) { + } catch { setTestCaseSummary(undefined); } }; @@ -274,7 +274,7 @@ const TableDetailsPageV1: React.FC = () => { entityId: tableDetails.id, }); setQueryCount(response.paging.total); - } catch (error) { + } catch { setQueryCount(0); } }; @@ -324,7 +324,7 @@ const TableDetailsPageV1: React.FC = () => { ); setTablePermissions(tablePermission); - } catch (error) { + } catch { showErrorToast( t('server.fetch-entity-permissions-error', { entity: t('label.resource-permission-lowercase'), @@ -664,8 +664,7 @@ const TableDetailsPageV1: React.FC = () => { }, [version, tableFqn]); const afterDeleteAction = useCallback( - (isSoftDelete?: boolean, version?: number) => - isSoftDelete ? handleToggleDelete(version) : history.push('/'), + (isSoftDelete?: boolean) => !isSoftDelete && history.push('/'), [] ); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/LocalStorageUtils.interface.ts b/openmetadata-ui/src/main/resources/ui/src/utils/LocalStorageUtils.interface.ts new file mode 100644 index 00000000000..bd61d6bc5b9 --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/utils/LocalStorageUtils.interface.ts @@ -0,0 +1,18 @@ +/* + * 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 { WorkflowStatus } from '../generated/governance/workflows/workflowInstance'; + +export interface AutoPilotStatus { + serviceFQN: string; + status: WorkflowStatus; +} diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/LocalStorageUtils.ts b/openmetadata-ui/src/main/resources/ui/src/utils/LocalStorageUtils.ts index a2408d0f197..4173ded70b3 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/LocalStorageUtils.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/LocalStorageUtils.ts @@ -10,7 +10,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { LOCAL_STORAGE_AUTO_PILOT_STATUS } from '../constants/LocalStorage.constants'; import { OM_SESSION_KEY } from '../hooks/useApplicationStore'; +import { AutoPilotStatus } from './LocalStorageUtils.interface'; export const getOidcToken = (): string => { return ( @@ -38,3 +40,38 @@ export const setRefreshToken = (token: string) => { session.refreshTokenKey = token; localStorage.setItem(OM_SESSION_KEY, JSON.stringify(session)); }; + +export const getAutoPilotStatuses = (): Array => { + return JSON.parse( + localStorage.getItem(LOCAL_STORAGE_AUTO_PILOT_STATUS) ?? '[]' + ); +}; + +export const updateAutoPilotStatus = (workflowStatus: AutoPilotStatus) => { + const currentStatuses = getAutoPilotStatuses(); + // Remove the status if it already exists for the serviceFQN + const filteredStatuses = currentStatuses.filter( + (status) => status.serviceFQN !== workflowStatus.serviceFQN + ); + // Add the new status + const updatedStatuses: Array = [ + ...filteredStatuses, + workflowStatus, + ]; + + localStorage.setItem( + LOCAL_STORAGE_AUTO_PILOT_STATUS, + JSON.stringify(updatedStatuses) + ); +}; + +export const removeAutoPilotStatus = (serviceFQN: string) => { + const currentStatuses = getAutoPilotStatuses(); + const filteredStatuses = currentStatuses.filter( + (status) => status.serviceFQN !== serviceFQN + ); + localStorage.setItem( + LOCAL_STORAGE_AUTO_PILOT_STATUS, + JSON.stringify(filteredStatuses) + ); +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/ServiceInsightsTabUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/ServiceInsightsTabUtils.tsx index c7b15e37c05..e12a0057cda 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/ServiceInsightsTabUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/ServiceInsightsTabUtils.tsx @@ -36,6 +36,7 @@ import i18n from '../utils/i18next/LocalUtil'; import { Transi18next } from './CommonUtils'; import documentationLinksClassBase from './DocumentationLinksClassBase'; import Fqn from './Fqn'; +import { getAutoPilotStatuses } from './LocalStorageUtils'; const { t } = i18n; @@ -296,3 +297,19 @@ export const filterDistributionChartItem = (item: { return toLower(tag_name) === toLower(item.group); }; + +export const checkIfAutoPilotStatusIsDismissed = ( + serviceFQN?: string, + workflowStatus?: WorkflowStatus +) => { + if (!serviceFQN || !workflowStatus) { + return false; + } + + const autoPilotStatuses = getAutoPilotStatuses(); + + return autoPilotStatuses.some( + (status) => + status.serviceFQN === serviceFQN && status.status === workflowStatus + ); +};