mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-12-13 00:22:23 +00:00
App widgets oss (#22743)
* update lineage provider * add plugin registry for applications * fix tests * add documentation for plugin
This commit is contained in:
parent
85aa2a8c2b
commit
1564b308be
@ -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 <Route key={idx} {...route} />;
|
||||
})}
|
||||
|
||||
<Route element={<Navigate to={ROUTES.MY_DATA} />} path={ROUTES.HOME} />
|
||||
<Route
|
||||
element={
|
||||
|
||||
@ -30,8 +30,8 @@ import { useCustomPages } from '../../../hooks/useCustomPages';
|
||||
import { filterHiddenNavigationItems } from '../../../utils/CustomizaNavigation/CustomizeNavigation';
|
||||
import { useAuthProvider } from '../../Auth/AuthProviders/AuthProvider';
|
||||
import BrandImage from '../../common/BrandImage/BrandImage';
|
||||
import { useApplicationsProvider } from '../../Settings/Applications/ApplicationsProvider/ApplicationsProvider';
|
||||
import './left-sidebar.less';
|
||||
import { LeftSidebarItem as LeftSidebarItemType } from './LeftSidebar.interface';
|
||||
import LeftSidebarItem from './LeftSidebarItem.component';
|
||||
const { Sider } = Layout;
|
||||
|
||||
@ -83,24 +83,45 @@ const LeftSidebar = () => {
|
||||
[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: <Icon component={item.icon} />,
|
||||
label: <LeftSidebarItem data={item} />,
|
||||
children: item.children?.map((item: LeftSidebarItemType) => {
|
||||
return {
|
||||
key: item.key,
|
||||
icon: <Icon component={item.icon} />,
|
||||
label: <LeftSidebarItem data={item} />,
|
||||
};
|
||||
}),
|
||||
};
|
||||
}),
|
||||
];
|
||||
}, [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: <Icon component={item.icon} />,
|
||||
label: <LeftSidebarItem data={item} />,
|
||||
children: item.children?.map((child) => ({
|
||||
key: child.key,
|
||||
icon: <Icon component={child.icon} />,
|
||||
label: <LeftSidebarItem data={child} />,
|
||||
})),
|
||||
}));
|
||||
}, [sideBarItems, pluginSidebarActions]);
|
||||
|
||||
const handleMenuClick: MenuProps['onClick'] = useCallback(() => {
|
||||
setOpenKeys([]);
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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
|
||||
<ApplicationConfiguration
|
||||
appData={appData}
|
||||
isLoading={loadingState.isSaveLoading}
|
||||
|
||||
@ -41,6 +41,10 @@ jest.mock('../../../../hooks/useFqn', () => ({
|
||||
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();
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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[];
|
||||
};
|
||||
|
||||
@ -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 (
|
||||
<ApplicationsContext.Provider value={appContext}>
|
||||
{loading ? <Loader /> : children}
|
||||
|
||||
@ -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<RouteProps>;
|
||||
|
||||
/**
|
||||
* 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<LeftSidebarItemExample>;
|
||||
}
|
||||
@ -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<void>;
|
||||
updateEntityFqn: (entityFqn: string) => void;
|
||||
dqHighlightedEdges?: Set<string>;
|
||||
}
|
||||
|
||||
@ -217,6 +217,21 @@ const LineageProvider = ({ children }: LineageProviderProps) => {
|
||||
const [isPlatformLineage, setIsPlatformLineage] = useState(false);
|
||||
const [dqHighlightedEdges, setDqHighlightedEdges] = useState<Set<string>>();
|
||||
|
||||
// Add state for entityFqn that can be updated independently of URL params
|
||||
const [entityFqn, setEntityFqn] = useState<string>(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 (
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user