mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-31 12:39:01 +00:00
Issue 10516 (#13297)
* fix: add description component for lineage * fix: update lineage data * fix: code smells --------- Co-authored-by: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com>
This commit is contained in:
parent
11ba94e4ea
commit
41262ecb59
@ -13,8 +13,10 @@
|
|||||||
|
|
||||||
import { CloseOutlined } from '@ant-design/icons';
|
import { CloseOutlined } from '@ant-design/icons';
|
||||||
import { Col, Divider, Drawer, Row, Typography } from 'antd';
|
import { Col, Divider, Drawer, Row, Typography } from 'antd';
|
||||||
|
import DescriptionV1 from 'components/common/description/DescriptionV1';
|
||||||
|
import { EntityType } from 'enums/entity.enum';
|
||||||
import { isUndefined } from 'lodash';
|
import { isUndefined } from 'lodash';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { Node } from 'reactflow';
|
import { Node } from 'reactflow';
|
||||||
@ -23,7 +25,6 @@ import { getEncodedFqn } from 'utils/StringsUtils';
|
|||||||
import { CSMode } from '../../../enums/codemirror.enum';
|
import { CSMode } from '../../../enums/codemirror.enum';
|
||||||
import { getNameFromFQN } from '../../../utils/CommonUtils';
|
import { getNameFromFQN } from '../../../utils/CommonUtils';
|
||||||
import { getEntityLink } from '../../../utils/TableUtils';
|
import { getEntityLink } from '../../../utils/TableUtils';
|
||||||
import RichTextEditorPreviewer from '../../common/rich-text-editor/RichTextEditorPreviewer';
|
|
||||||
import Loader from '../../Loader/Loader';
|
import Loader from '../../Loader/Loader';
|
||||||
import SchemaEditor from '../../schema-editor/SchemaEditor';
|
import SchemaEditor from '../../schema-editor/SchemaEditor';
|
||||||
import {
|
import {
|
||||||
@ -37,12 +38,21 @@ const EdgeInfoDrawer = ({
|
|||||||
visible,
|
visible,
|
||||||
onClose,
|
onClose,
|
||||||
nodes,
|
nodes,
|
||||||
|
hasEditAccess,
|
||||||
|
onEdgeDescriptionUpdate,
|
||||||
}: EdgeInfoDrawerInfo) => {
|
}: EdgeInfoDrawerInfo) => {
|
||||||
const [edgeData, setEdgeData] = useState<EdgeInformationType>();
|
const [edgeData, setEdgeData] = useState<EdgeInformationType>();
|
||||||
const [mysqlQuery, setMysqlQuery] = useState('');
|
const [mysqlQuery, setMysqlQuery] = useState('');
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [isDescriptionEditable, setIsDescriptionEditable] =
|
||||||
|
useState<boolean>(false);
|
||||||
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const edgeEntity = useMemo(() => {
|
||||||
|
return edge.data.edge;
|
||||||
|
}, [edge]);
|
||||||
|
|
||||||
const getEdgeInfo = () => {
|
const getEdgeInfo = () => {
|
||||||
const { source, target, data, sourceHandle, targetHandle } = edge;
|
const { source, target, data, sourceHandle, targetHandle } = edge;
|
||||||
let sourceData: Node | undefined, targetData: Node | undefined;
|
let sourceData: Node | undefined, targetData: Node | undefined;
|
||||||
@ -101,6 +111,39 @@ const EdgeInfoDrawer = ({
|
|||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const edgeDescription = useMemo(() => {
|
||||||
|
return edgeEntity?.lineageDetails?.description ?? '';
|
||||||
|
}, [edgeEntity]);
|
||||||
|
|
||||||
|
const onDescriptionUpdate = useCallback(
|
||||||
|
async (updatedHTML: string) => {
|
||||||
|
if (edgeDescription !== updatedHTML && edgeEntity) {
|
||||||
|
const lineageDetails = {
|
||||||
|
...edgeEntity.lineageDetails,
|
||||||
|
description: updatedHTML,
|
||||||
|
};
|
||||||
|
const updatedEdgeDetails = {
|
||||||
|
edge: {
|
||||||
|
fromEntity: {
|
||||||
|
id: edgeEntity.fromEntity,
|
||||||
|
type: edge.data.sourceType,
|
||||||
|
},
|
||||||
|
toEntity: {
|
||||||
|
id: edgeEntity.toEntity,
|
||||||
|
type: edge.data.sourceType,
|
||||||
|
},
|
||||||
|
lineageDetails,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
await onEdgeDescriptionUpdate(updatedEdgeDetails);
|
||||||
|
setIsDescriptionEditable(false);
|
||||||
|
} else {
|
||||||
|
setIsDescriptionEditable(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[edgeDescription, edgeEntity, edge.data]
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
getEdgeInfo();
|
getEdgeInfo();
|
||||||
@ -145,20 +188,17 @@ const EdgeInfoDrawer = ({
|
|||||||
)}
|
)}
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<Divider />
|
<Divider />
|
||||||
<Typography.Paragraph className="summary-panel-section-title">
|
<DescriptionV1
|
||||||
{`${t('label.description')}:`}
|
description={edgeDescription}
|
||||||
</Typography.Paragraph>
|
entityName="Edge"
|
||||||
{edge?.data.edge?.description?.trim() ? (
|
entityType={EntityType.GLOSSARY}
|
||||||
<RichTextEditorPreviewer
|
hasEditAccess={hasEditAccess}
|
||||||
markdown={edge?.data.edge?.description}
|
isEdit={isDescriptionEditable}
|
||||||
/>
|
showCommentsIcon={false}
|
||||||
) : (
|
onCancel={() => setIsDescriptionEditable(false)}
|
||||||
<Typography.Paragraph className=" m-b-0">
|
onDescriptionEdit={() => setIsDescriptionEditable(true)}
|
||||||
{t('label.no-entity', {
|
onDescriptionUpdate={onDescriptionUpdate}
|
||||||
entity: t('label.description'),
|
/>
|
||||||
})}
|
|
||||||
</Typography.Paragraph>
|
|
||||||
)}
|
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<Divider />
|
<Divider />
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { AddLineage } from 'generated/api/lineage/addLineage';
|
||||||
import { Edge, Node } from 'reactflow';
|
import { Edge, Node } from 'reactflow';
|
||||||
import { SelectedNode } from '../EntityLineage/EntityLineage.interface';
|
import { SelectedNode } from '../EntityLineage/EntityLineage.interface';
|
||||||
|
|
||||||
@ -25,7 +26,9 @@ export interface EdgeInfoDrawerInfo {
|
|||||||
edge: Edge;
|
edge: Edge;
|
||||||
nodes: Node[];
|
nodes: Node[];
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
|
hasEditAccess: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
|
onEdgeDescriptionUpdate: (updatedEdgeDetails: AddLineage) => Promise<void>;
|
||||||
}
|
}
|
||||||
type InfoType = {
|
type InfoType = {
|
||||||
key: string;
|
key: string;
|
||||||
|
@ -65,7 +65,7 @@ import ReactFlow, {
|
|||||||
useNodesState,
|
useNodesState,
|
||||||
} from 'reactflow';
|
} from 'reactflow';
|
||||||
import { getDataModelDetails } from 'rest/dataModelsAPI';
|
import { getDataModelDetails } from 'rest/dataModelsAPI';
|
||||||
import { getLineageByFQN } from 'rest/lineageAPI';
|
import { getLineageByFQN, updateLineageEdge } from 'rest/lineageAPI';
|
||||||
import { searchData } from 'rest/miscAPI';
|
import { searchData } from 'rest/miscAPI';
|
||||||
import { getTableDetails } from 'rest/tableAPI';
|
import { getTableDetails } from 'rest/tableAPI';
|
||||||
import {
|
import {
|
||||||
@ -106,6 +106,7 @@ import {
|
|||||||
onNodeMouseLeave,
|
onNodeMouseLeave,
|
||||||
onNodeMouseMove,
|
onNodeMouseMove,
|
||||||
removeLineageHandler,
|
removeLineageHandler,
|
||||||
|
updateEdgesWithLineageDetails,
|
||||||
} from 'utils/EntityLineageUtils';
|
} from 'utils/EntityLineageUtils';
|
||||||
import {
|
import {
|
||||||
getEntityBreadcrumbs,
|
getEntityBreadcrumbs,
|
||||||
@ -1482,6 +1483,56 @@ const EntityLineageComponent: FunctionComponent<EntityLineageProp> = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onEdgeDescriptionUpdate = useCallback(
|
||||||
|
async (updatedEdgeDetails: AddLineage) => {
|
||||||
|
try {
|
||||||
|
await updateLineageEdge(updatedEdgeDetails);
|
||||||
|
if (selectedEdgeInfo) {
|
||||||
|
const updatedSelectedEdgeInfo = {
|
||||||
|
...selectedEdgeInfo,
|
||||||
|
data: {
|
||||||
|
...selectedEdgeInfo.data,
|
||||||
|
edge: {
|
||||||
|
...selectedEdgeInfo.data.edge,
|
||||||
|
lineageDetails: updatedEdgeDetails.edge.lineageDetails,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const updatedEdges = edges.map((edge) =>
|
||||||
|
edge.id === selectedEdgeInfo.id ? updatedSelectedEdgeInfo : edge
|
||||||
|
);
|
||||||
|
|
||||||
|
setEdges(updatedEdges);
|
||||||
|
setSelectedEdgeInfo(updatedSelectedEdgeInfo);
|
||||||
|
|
||||||
|
setUpdatedLineageData((pre) => {
|
||||||
|
if (!pre) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newData = {
|
||||||
|
...pre,
|
||||||
|
downstreamEdges: updateEdgesWithLineageDetails(
|
||||||
|
pre.downstreamEdges ?? [],
|
||||||
|
updatedEdgeDetails
|
||||||
|
),
|
||||||
|
upstreamEdges: updateEdgesWithLineageDetails(
|
||||||
|
pre.upstreamEdges ?? [],
|
||||||
|
updatedEdgeDetails
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
return newData;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
showErrorToast(err as AxiosError);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[edges, selectedEdgeInfo, updatedLineageData, setUpdatedLineageData]
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle updated lineage nodes
|
* Handle updated lineage nodes
|
||||||
* Change newly added node label based on entity:EntityReference
|
* Change newly added node label based on entity:EntityReference
|
||||||
@ -1681,12 +1732,14 @@ const EntityLineageComponent: FunctionComponent<EntityLineageProp> = ({
|
|||||||
(selectedEdgeInfo ? (
|
(selectedEdgeInfo ? (
|
||||||
<EdgeInfoDrawer
|
<EdgeInfoDrawer
|
||||||
edge={selectedEdgeInfo}
|
edge={selectedEdgeInfo}
|
||||||
|
hasEditAccess={hasEditAccess}
|
||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
visible={isDrawerOpen}
|
visible={isDrawerOpen}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
setIsDrawerOpen(false);
|
setIsDrawerOpen(false);
|
||||||
setSelectedEdgeInfo(undefined);
|
setSelectedEdgeInfo(undefined);
|
||||||
}}
|
}}
|
||||||
|
onEdgeDescriptionUpdate={onEdgeDescriptionUpdate}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<EntityInfoDrawer
|
<EntityInfoDrawer
|
||||||
|
@ -32,7 +32,7 @@ export interface SelectedNode {
|
|||||||
export interface EntityLineageProp {
|
export interface EntityLineageProp {
|
||||||
entityType: EntityType;
|
entityType: EntityType;
|
||||||
deleted?: boolean;
|
deleted?: boolean;
|
||||||
hasEditAccess?: boolean;
|
hasEditAccess: boolean;
|
||||||
isFullScreen?: boolean;
|
isFullScreen?: boolean;
|
||||||
entity?: SourceType;
|
entity?: SourceType;
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { AddLineage } from 'generated/api/lineage/addLineage';
|
||||||
import { EntityLineage } from '../generated/type/entityLineage';
|
import { EntityLineage } from '../generated/type/entityLineage';
|
||||||
import APIClient from './index';
|
import APIClient from './index';
|
||||||
|
|
||||||
@ -26,3 +27,9 @@ export const getLineageByFQN = async (
|
|||||||
|
|
||||||
return response.data;
|
return response.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const updateLineageEdge = async (edge: AddLineage) => {
|
||||||
|
const response = await APIClient.put<AddLineage>(`/lineage`, edge);
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
@ -1372,3 +1372,16 @@ export const getSearchIndexFromNodeType = (entityType: string) => {
|
|||||||
|
|
||||||
return SearchIndex[searchIndexKey] as ExploreSearchIndex;
|
return SearchIndex[searchIndexKey] as ExploreSearchIndex;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const updateEdgesWithLineageDetails = (
|
||||||
|
edgesArray: EntityLineageEdge[],
|
||||||
|
updatedEdgeDetails: AddLineage
|
||||||
|
) => {
|
||||||
|
const { fromEntity, toEntity, lineageDetails } = updatedEdgeDetails.edge;
|
||||||
|
|
||||||
|
return edgesArray.map((item) =>
|
||||||
|
item.toEntity === toEntity.id && item.fromEntity === fromEntity.id
|
||||||
|
? { ...item, lineageDetails: lineageDetails }
|
||||||
|
: item
|
||||||
|
);
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user