From 1564b308be33c1e73bff622a06d889fe2a6bc407 Mon Sep 17 00:00:00 2001
From: Karan Hotchandani <33024356+karanh37@users.noreply.github.com>
Date: Mon, 11 Aug 2025 11:55:06 +0530
Subject: [PATCH] App widgets oss (#22743)
* update lineage provider
* add plugin registry for applications
* fix tests
* add documentation for plugin
---
.../AppRouter/AuthenticatedAppRouter.tsx | 8 ++
.../LeftSidebar/LeftSidebar.component.tsx | 57 +++++++++-----
.../MyData/LeftSidebar/LeftSidebar.test.tsx | 7 ++
.../AppDetails/AppDetails.component.tsx | 21 ++++-
.../AppDetails/AppDetails.test.tsx | 4 +
.../AppDetails/ApplicationsClassBase.ts | 6 ++
.../ApplicationsProvider.interface.ts | 2 +
.../ApplicationsProvider.tsx | 22 +++++-
.../Applications/plugins/AppPlugin.ts | 66 ++++++++++++++++
.../LineageProvider.interface.tsx | 2 +
.../LineageProvider/LineageProvider.tsx | 76 +++++++++++++------
11 files changed, 224 insertions(+), 47 deletions(-)
create mode 100644 openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/plugins/AppPlugin.ts
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/AuthenticatedAppRouter.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/AuthenticatedAppRouter.tsx
index 891fd58e1db..b9b1a99317e 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/AuthenticatedAppRouter.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/AuthenticatedAppRouter.tsx
@@ -28,6 +28,7 @@ import ForbiddenPage from '../../pages/ForbiddenPage/ForbiddenPage';
import PlatformLineage from '../../pages/PlatformLineage/PlatformLineage';
import TagPage from '../../pages/TagPage/TagPage';
import { checkPermission, userPermissions } from '../../utils/PermissionsUtils';
+import { useApplicationsProvider } from '../Settings/Applications/ApplicationsProvider/ApplicationsProvider';
import AdminProtectedRoute from './AdminProtectedRoute';
import withSuspenseFallback from './withSuspenseFallback';
@@ -278,7 +279,9 @@ const AddMetricPage = withSuspenseFallback(
const AuthenticatedAppRouter: FunctionComponent = () => {
const { permissions } = usePermissionProvider();
const { t } = useTranslation();
+ const { plugins } = useApplicationsProvider();
+ const pluginRoutes = plugins.flatMap((plugin) => plugin.getRoutes?.() || []);
const createBotPermission = useMemo(
() =>
checkPermission(Operation.Create, ResourceEntity.USER, permissions) &&
@@ -686,6 +689,11 @@ const AuthenticatedAppRouter: FunctionComponent = () => {
path={ROUTES.EDIT_KPI}
/>
+ {/* Plugin routes */}
+ {pluginRoutes.map((route, idx) => {
+ return ;
+ })}
+
} path={ROUTES.HOME} />
{
[handleLogoutClick]
);
+ const { plugins } = useApplicationsProvider();
+
+ const pluginSidebarActions = useMemo(() => {
+ return plugins
+ .flatMap((plugin) => plugin.getSidebarActions?.() ?? [])
+ .sort((a, b) => (a.index ?? 999) - (b.index ?? 999));
+ }, [plugins]);
+
const menuItems = useMemo(() => {
- return [
- ...sideBarItems.map((item) => {
- return {
- key: item.key,
- icon: ,
- label: ,
- children: item.children?.map((item: LeftSidebarItemType) => {
- return {
- key: item.key,
- icon: ,
- label: ,
- };
- }),
- };
- }),
- ];
- }, [sideBarItems]);
+ const mergedItems = (() => {
+ const baseItems = [...sideBarItems];
+
+ pluginSidebarActions.forEach((pluginItem) => {
+ if (typeof pluginItem.index === 'number' && pluginItem.index >= 0) {
+ baseItems.splice(
+ Math.min(pluginItem.index, baseItems.length),
+ 0,
+ pluginItem
+ );
+ } else {
+ baseItems.push(pluginItem);
+ }
+ });
+
+ return baseItems;
+ })();
+
+ // Map to menu structure
+ return mergedItems.map((item) => ({
+ key: item.key,
+ icon: ,
+ label: ,
+ children: item.children?.map((child) => ({
+ key: child.key,
+ icon: ,
+ label: ,
+ })),
+ }));
+ }, [sideBarItems, pluginSidebarActions]);
const handleMenuClick: MenuProps['onClick'] = useCallback(() => {
setOpenKeys([]);
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/LeftSidebar.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/LeftSidebar.test.tsx
index 6c35e78771f..786e1daa143 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/LeftSidebar.test.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/LeftSidebar/LeftSidebar.test.tsx
@@ -14,6 +14,13 @@ import { render, screen } from '@testing-library/react';
import { BrowserRouter } from 'react-router-dom';
import LeftSidebar from './LeftSidebar.component';
+jest.mock(
+ '../../Settings/Applications/ApplicationsProvider/ApplicationsProvider',
+ () => ({
+ useApplicationsProvider: () => ({ applications: [], plugins: [] }),
+ })
+);
+
describe('LeftSidebar', () => {
it('renders sidebar links correctly', () => {
render(
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/AppDetails/AppDetails.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/AppDetails/AppDetails.component.tsx
index 0bc2109fbba..aa35e1f600c 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/AppDetails/AppDetails.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/AppDetails/AppDetails.component.tsx
@@ -33,7 +33,7 @@ import { ItemType } from 'antd/lib/menu/hooks/useItems';
import { AxiosError } from 'axios';
import { compare } from 'fast-json-patch';
import { isEmpty } from 'lodash';
-import { useCallback, useEffect, useMemo, useState } from 'react';
+import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import { ReactComponent as IconExternalLink } from '../../../../assets/svg/external-links.svg';
@@ -71,6 +71,7 @@ import TabsLabel from '../../../common/TabsLabel/TabsLabel.component';
import ConfirmationModal from '../../../Modals/ConfirmationModal/ConfirmationModal';
import PageLayoutV1 from '../../../PageLayoutV1/PageLayoutV1';
import ApplicationConfiguration from '../ApplicationConfiguration/ApplicationConfiguration';
+import { useApplicationsProvider } from '../ApplicationsProvider/ApplicationsProvider';
import AppLogo from '../AppLogo/AppLogo.component';
import AppRunsHistory from '../AppRunsHistory/AppRunsHistory.component';
import AppSchedule from '../AppSchedule/AppSchedule.component';
@@ -95,6 +96,7 @@ const AppDetails = () => {
isSaveLoading: false,
});
const { getResourceLimit } = useLimitStore();
+ const { plugins } = useApplicationsProvider();
const fetchAppDetails = useCallback(async () => {
setLoadingState((prev) => ({ ...prev, isFetchLoading: true }));
@@ -323,6 +325,17 @@ const AppDetails = () => {
}
};
+ // Check if there's a plugin configuration component for this app
+ const pluginConfigComponent = useMemo(() => {
+ if (!appData?.name || !plugins.length) {
+ return null;
+ }
+
+ const plugin = plugins.find((p) => p.name === appData.name);
+
+ return plugin?.getConfigComponent?.(appData) || null;
+ }, [appData?.name, plugins]);
+
const tabs = useMemo(() => {
const tabConfiguration =
appData?.appConfiguration && appData.allowConfiguration && jsonSchema
@@ -335,7 +348,11 @@ const AppDetails = () => {
/>
),
key: ApplicationTabs.CONFIGURATION,
- children: (
+ children: pluginConfigComponent ? (
+ // Use plugin configuration component if available
+ React.createElement(pluginConfigComponent)
+ ) : (
+ // Fall back to default ApplicationConfiguration
({
useFqn: jest.fn().mockReturnValue({ fqn: 'mockFQN' }),
}));
+jest.mock('../ApplicationsProvider/ApplicationsProvider', () => ({
+ useApplicationsProvider: () => ({ applications: [], plugins: [] }),
+}));
+
const mockConfigureApp = jest.fn();
const mockDeployApp = jest.fn();
const mockRestoreApp = jest.fn();
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/AppDetails/ApplicationsClassBase.ts b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/AppDetails/ApplicationsClassBase.ts
index 123bdf2aa4a..1f18ee6fa98 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/AppDetails/ApplicationsClassBase.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/AppDetails/ApplicationsClassBase.ts
@@ -14,6 +14,7 @@
import { FC } from 'react';
import { AppType } from '../../../../generated/entity/applications/app';
import { getScheduleOptionsFromSchedules } from '../../../../utils/SchedularUtils';
+import { AppPlugin } from '../plugins/AppPlugin';
class ApplicationsClassBase {
public importSchema(fqn: string) {
@@ -56,6 +57,11 @@ class ApplicationsClassBase {
return import(`../../../../assets/img/appScreenshots/${screenshotName}`);
}
+ public appPluginRegistry: Record<
+ string,
+ new (name: string, isInstalled: boolean) => AppPlugin
+ > = {};
+
public getScheduleOptionsForApp(
app: string,
appType: AppType,
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/ApplicationsProvider/ApplicationsProvider.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/ApplicationsProvider/ApplicationsProvider.interface.ts
index b758d9cc6e8..05b6bfe3be2 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/ApplicationsProvider/ApplicationsProvider.interface.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/ApplicationsProvider/ApplicationsProvider.interface.ts
@@ -11,7 +11,9 @@
* limitations under the License.
*/
import { EntityReference } from '../../../../generated/entity/type';
+import type { AppPlugin } from '../plugins/AppPlugin';
export type ApplicationsContextType = {
applications: EntityReference[];
+ plugins: AppPlugin[];
};
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/ApplicationsProvider/ApplicationsProvider.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/ApplicationsProvider/ApplicationsProvider.tsx
index 3f4596b7351..dbbfdfebac4 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/ApplicationsProvider/ApplicationsProvider.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/ApplicationsProvider/ApplicationsProvider.tsx
@@ -25,6 +25,8 @@ import { EntityReference } from '../../../../generated/entity/type';
import { useApplicationStore } from '../../../../hooks/useApplicationStore';
import { getInstalledApplicationList } from '../../../../rest/applicationAPI';
import Loader from '../../../common/Loader/Loader';
+import applicationsClassBase from '../AppDetails/ApplicationsClassBase';
+import type { AppPlugin } from '../plugins/AppPlugin';
import { ApplicationsContextType } from './ApplicationsProvider.interface';
export const ApplicationsContext = createContext({} as ApplicationsContextType);
@@ -45,7 +47,7 @@ export const ApplicationsProvider = ({ children }: { children: ReactNode }) => {
(app) => app.name ?? app.fullyQualifiedName ?? ''
);
setApplicationsName(applicationsNameList);
- } catch (err) {
+ } catch {
// do not handle error
} finally {
setLoading(false);
@@ -60,10 +62,24 @@ export const ApplicationsProvider = ({ children }: { children: ReactNode }) => {
}
}, []);
- const appContext = useMemo(() => {
- return { applications };
+ const installedPluginInstances: AppPlugin[] = useMemo(() => {
+ return applications
+ .map((app) => {
+ if (!app.name) {
+ return null;
+ }
+
+ const PluginClass = applicationsClassBase.appPluginRegistry[app.name];
+
+ return PluginClass ? new PluginClass(app.name, true) : null;
+ })
+ .filter(Boolean) as AppPlugin[];
}, [applications]);
+ const appContext = useMemo(() => {
+ return { applications, plugins: installedPluginInstances };
+ }, [applications, installedPluginInstances]);
+
return (
{loading ? : children}
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/plugins/AppPlugin.ts b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/plugins/AppPlugin.ts
new file mode 100644
index 00000000000..00b847a9f99
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/plugins/AppPlugin.ts
@@ -0,0 +1,66 @@
+/*
+ * 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 { FC } from 'react';
+import { RouteProps } from 'react-router-dom';
+import { App } from '../../../../generated/entity/applications/app';
+import { LeftSidebarItem } from '../../../MyData/LeftSidebar/LeftSidebar.interface';
+
+export interface LeftSidebarItemExample extends LeftSidebarItem {
+ index: number;
+}
+
+/**
+ * Interface defining the structure and capabilities of an application plugin.
+ *
+ * This interface allows plugins to extend the OpenMetadata application with
+ * custom components, routes, and sidebar actions. Plugins can be installed
+ * or uninstalled dynamically and provide modular functionality.
+ */
+export interface AppPlugin {
+ /**
+ * The unique name of the app received from the /apps endpoint.
+ */
+ name: string;
+
+ /**
+ * Indicates whether the app is currently installed and active.
+ * Used to determine plugin availability and UI state.
+ */
+ isInstalled: boolean;
+
+ /**
+ * Optional method that returns a React component for plugin configuration.
+ * It is the responsibility of this component to update application data using patchApplication API
+ *
+ * @param app - The App entity containing application details and configuration
+ * @returns A React functional component for plugin settings/configuration,
+ * or null if no configuration is needed.
+ */
+ getConfigComponent?(app: App): FC | null;
+
+ /**
+ * Optional method that provides custom routes for the plugin.
+ *
+ * @returns An array of route properties that define the plugin's
+ * navigation structure and page routing.
+ */
+ getRoutes?(): Array;
+
+ /**
+ * Optional method that provides custom sidebar actions for the plugin.
+ *
+ * @returns An array of sidebar items that will be displayed in the
+ * left sidebar when the plugin is active.
+ */
+ getSidebarActions?(): Array;
+}
diff --git a/openmetadata-ui/src/main/resources/ui/src/context/LineageProvider/LineageProvider.interface.tsx b/openmetadata-ui/src/main/resources/ui/src/context/LineageProvider/LineageProvider.interface.tsx
index 3e1755024c2..1c0d9454551 100644
--- a/openmetadata-ui/src/main/resources/ui/src/context/LineageProvider/LineageProvider.interface.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/context/LineageProvider/LineageProvider.interface.tsx
@@ -61,6 +61,7 @@ export interface LineageContextType {
platformView: LineagePlatformView;
expandAllColumns: boolean;
isPlatformLineage: boolean;
+ entityFqn: string;
toggleColumnView: () => void;
onInitReactFlow: (reactFlowInstance: ReactFlowInstance) => void;
onPaneClick: () => void;
@@ -98,5 +99,6 @@ export interface LineageContextType {
) => void;
onUpdateLayerView: (layers: LineageLayer[]) => void;
redraw: () => Promise;
+ updateEntityFqn: (entityFqn: string) => void;
dqHighlightedEdges?: Set;
}
diff --git a/openmetadata-ui/src/main/resources/ui/src/context/LineageProvider/LineageProvider.tsx b/openmetadata-ui/src/main/resources/ui/src/context/LineageProvider/LineageProvider.tsx
index 61a6a83e8b5..ffe3fa6d28d 100644
--- a/openmetadata-ui/src/main/resources/ui/src/context/LineageProvider/LineageProvider.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/context/LineageProvider/LineageProvider.tsx
@@ -217,6 +217,21 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
const [isPlatformLineage, setIsPlatformLineage] = useState(false);
const [dqHighlightedEdges, setDqHighlightedEdges] = useState>();
+ // Add state for entityFqn that can be updated independently of URL params
+ const [entityFqn, setEntityFqn] = useState(decodedFqn);
+
+ // Update entityFqn when decodedFqn changes (for backward compatibility)
+ useEffect(() => {
+ if (decodedFqn) {
+ setEntityFqn(decodedFqn);
+ }
+ }, [decodedFqn]);
+
+ // Function to update entityFqn
+ const updateEntityFqn = useCallback((fqn: string) => {
+ setEntityFqn(fqn);
+ }, []);
+
const lineageLayer = useMemo(() => {
const param = location.search;
const searchData = QueryString.parse(
@@ -276,7 +291,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
} = createEdgesAndEdgeMaps(
allNodes,
lineageData.edges ?? [],
- decodedFqn,
+ entityFqn,
activeLayer.includes(LineageLayer.ColumnLevelLineage),
isFirstTime ? true : undefined
);
@@ -284,7 +299,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
const initialNodes = createNodes(
allNodes,
lineageData.edges ?? [],
- decodedFqn,
+ entityFqn,
incomingMap,
outgoingMap,
activeLayer.includes(LineageLayer.ColumnLevelLineage),
@@ -334,7 +349,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
setColumnsHavingLineage(columnsHavingLineage);
},
[
- decodedFqn,
+ entityFqn,
activeLayer,
isEditMode,
reactFlowInstance,
@@ -379,7 +394,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
setLineageData(res);
- const { nodes, edges, entity } = parseLineageData(res, '', decodedFqn);
+ const { nodes, edges, entity } = parseLineageData(res, '', entityFqn);
const updatedEntityLineage = {
nodes,
edges,
@@ -399,7 +414,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
setLoading(false);
}
},
- []
+ [entityFqn]
);
const fetchLineageData = useCallback(
@@ -423,7 +438,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
});
setLineageData(res);
- const { nodes, edges, entity } = parseLineageData(res, fqn, decodedFqn);
+ const { nodes, edges, entity } = parseLineageData(res, fqn, entityFqn);
const updatedEntityLineage = {
nodes,
edges,
@@ -443,7 +458,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
setLoading(false);
}
},
- [queryFilter, decodedFqn]
+ [queryFilter, entityFqn]
);
const onPlatformViewChange = useCallback(
@@ -473,17 +488,17 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
const exportLineageData = useCallback(
async (_: string) => {
return exportLineageAsync(
- decodedFqn,
+ entityFqn,
entityType,
lineageConfig,
queryFilter
);
},
- [entityType, decodedFqn, lineageConfig, queryFilter]
+ [entityType, entityFqn, lineageConfig, queryFilter]
);
const onExportClick = useCallback(() => {
- if (decodedFqn || isPlatformLineagePage) {
+ if (entityFqn || isPlatformLineagePage) {
showModal({
...(isPlatformLineagePage
? {
@@ -491,7 +506,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
exportTypes: [ExportTypes.PNG],
}
: {
- name: decodedFqn,
+ name: entityFqn,
exportTypes: [ExportTypes.CSV, ExportTypes.PNG],
}),
title: t('label.lineage'),
@@ -502,7 +517,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
}
}, [
entityType,
- decodedFqn,
+ entityFqn,
lineageConfig,
queryFilter,
nodes,
@@ -552,7 +567,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
const { nodes: newNodes, edges: newEdges } = parseLineageData(
concatenatedLineageData,
node.fullyQualifiedName ?? '',
- decodedFqn
+ entityFqn
);
const uniqueNodes = [...(entityLineage.nodes ?? [])];
@@ -625,7 +640,15 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
);
}
},
- [nodes, edges, lineageConfig, entityLineage, setEntityLineage, queryFilter]
+ [
+ nodes,
+ edges,
+ lineageConfig,
+ entityLineage,
+ setEntityLineage,
+ queryFilter,
+ entityFqn,
+ ]
);
const handleLineageTracing = useCallback(
@@ -686,6 +709,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
isPlatformLineage?: boolean
) => {
setEntity(entity);
+ setEntityFqn(entity?.fullyQualifiedName ?? '');
setEntityType(entityType);
setIsPlatformLineage(isPlatformLineage ?? false);
if (isPlatformLineage && !entity) {
@@ -958,7 +982,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
const { nodes: newNodes, edges: newEdges } = parseLineageData(
concatenatedLineageData,
parentNode.data.node.fullyQualifiedName,
- decodedFqn
+ entityFqn
);
updateLineageData(
@@ -1174,7 +1198,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
createEdgesAndEdgeMaps(
allNodes,
allEdges,
- decodedFqn,
+ entityFqn,
activeLayer.includes(LineageLayer.ColumnLevelLineage)
);
setEdges(createdEdges);
@@ -1191,7 +1215,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
});
}
},
- [entityLineage, nodes, decodedFqn]
+ [entityLineage, nodes, entityFqn]
);
const onAddPipelineClick = useCallback(() => {
@@ -1499,7 +1523,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
}, [entityLineage, redrawLineage]);
const onPlatformViewUpdate = useCallback(() => {
- if (entity && decodedFqn && entityType) {
+ if (entity && entityFqn && entityType) {
if (platformView === LineagePlatformView.Service && entity?.service) {
fetchLineageData(
entity?.service.fullyQualifiedName ?? '',
@@ -1525,7 +1549,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
lineageConfig
);
} else if (platformView === LineagePlatformView.None) {
- fetchLineageData(decodedFqn, entityType, lineageConfig);
+ fetchLineageData(entityFqn, entityType, lineageConfig);
} else if (isPlatformLineage) {
fetchPlatformLineage(
getEntityTypeFromPlatformView(platformView),
@@ -1541,7 +1565,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
}, [
entity,
entityType,
- decodedFqn,
+ entityFqn,
lineageConfig,
platformView,
queryFilter,
@@ -1571,7 +1595,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
getUpstreamDownstreamNodesEdges(
updatedEntityLineage.edges ?? [],
updatedEntityLineage.nodes ?? [],
- decodedFqn
+ entityFqn
);
const updatedNodes =
@@ -1588,7 +1612,7 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
nodes: updatedNodes as EntityReference[],
});
}
- }, [isEditMode, updatedEntityLineage, decodedFqn]);
+ }, [isEditMode, updatedEntityLineage, entityFqn]);
useEffect(() => {
if (isEditMode) {
@@ -1658,6 +1682,8 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
expandAllColumns,
platformView,
isPlatformLineage,
+ entityFqn,
+ updateEntityFqn,
toggleColumnView,
onInitReactFlow,
onPaneClick,
@@ -1708,6 +1734,8 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
columnsHavingLineage,
expandAllColumns,
isPlatformLineage,
+ entityFqn,
+ updateEntityFqn,
toggleColumnView,
onInitReactFlow,
onPaneClick,
@@ -1755,9 +1783,9 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
useEffect(() => {
if (activeLayer.includes(LineageLayer.DataObservability)) {
- fetchDataQualityLineage(decodedFqn, lineageConfig);
+ fetchDataQualityLineage(entityFqn, lineageConfig);
}
- }, [activeLayer, decodedFqn, lineageConfig]);
+ }, [activeLayer, entityFqn, lineageConfig]);
useEffect(() => {
if (