diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityInfoDrawer/EdgeInfoDrawer.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityInfoDrawer/EdgeInfoDrawer.component.tsx index 12e75093393..e5defd953b9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityInfoDrawer/EdgeInfoDrawer.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityInfoDrawer/EdgeInfoDrawer.component.tsx @@ -13,8 +13,10 @@ import { CloseOutlined } from '@ant-design/icons'; 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 React, { useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom'; import { Node } from 'reactflow'; @@ -23,7 +25,6 @@ import { getEncodedFqn } from 'utils/StringsUtils'; import { CSMode } from '../../../enums/codemirror.enum'; import { getNameFromFQN } from '../../../utils/CommonUtils'; import { getEntityLink } from '../../../utils/TableUtils'; -import RichTextEditorPreviewer from '../../common/rich-text-editor/RichTextEditorPreviewer'; import Loader from '../../Loader/Loader'; import SchemaEditor from '../../schema-editor/SchemaEditor'; import { @@ -37,12 +38,21 @@ const EdgeInfoDrawer = ({ visible, onClose, nodes, + hasEditAccess, + onEdgeDescriptionUpdate, }: EdgeInfoDrawerInfo) => { const [edgeData, setEdgeData] = useState(); const [mysqlQuery, setMysqlQuery] = useState(''); const [isLoading, setIsLoading] = useState(false); + const [isDescriptionEditable, setIsDescriptionEditable] = + useState(false); + const { t } = useTranslation(); + const edgeEntity = useMemo(() => { + return edge.data.edge; + }, [edge]); + const getEdgeInfo = () => { const { source, target, data, sourceHandle, targetHandle } = edge; let sourceData: Node | undefined, targetData: Node | undefined; @@ -101,6 +111,39 @@ const EdgeInfoDrawer = ({ 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(() => { setIsLoading(true); getEdgeInfo(); @@ -145,20 +188,17 @@ const EdgeInfoDrawer = ({ )} - - {`${t('label.description')}:`} - - {edge?.data.edge?.description?.trim() ? ( - - ) : ( - - {t('label.no-entity', { - entity: t('label.description'), - })} - - )} + setIsDescriptionEditable(false)} + onDescriptionEdit={() => setIsDescriptionEditable(true)} + onDescriptionUpdate={onDescriptionUpdate} + /> diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityInfoDrawer/EntityInfoDrawer.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityInfoDrawer/EntityInfoDrawer.interface.ts index 669e2b3f122..dd5f3aaa260 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityInfoDrawer/EntityInfoDrawer.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityInfoDrawer/EntityInfoDrawer.interface.ts @@ -11,6 +11,7 @@ * limitations under the License. */ +import { AddLineage } from 'generated/api/lineage/addLineage'; import { Edge, Node } from 'reactflow'; import { SelectedNode } from '../EntityLineage/EntityLineage.interface'; @@ -25,7 +26,9 @@ export interface EdgeInfoDrawerInfo { edge: Edge; nodes: Node[]; visible: boolean; + hasEditAccess: boolean; onClose: () => void; + onEdgeDescriptionUpdate: (updatedEdgeDetails: AddLineage) => Promise; } type InfoType = { key: string; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityLineage/EntityLineage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityLineage/EntityLineage.component.tsx index adedb325970..e158aa17ffb 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityLineage/EntityLineage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityLineage/EntityLineage.component.tsx @@ -65,7 +65,7 @@ import ReactFlow, { useNodesState, } from 'reactflow'; import { getDataModelDetails } from 'rest/dataModelsAPI'; -import { getLineageByFQN } from 'rest/lineageAPI'; +import { getLineageByFQN, updateLineageEdge } from 'rest/lineageAPI'; import { searchData } from 'rest/miscAPI'; import { getTableDetails } from 'rest/tableAPI'; import { @@ -106,6 +106,7 @@ import { onNodeMouseLeave, onNodeMouseMove, removeLineageHandler, + updateEdgesWithLineageDetails, } from 'utils/EntityLineageUtils'; import { getEntityBreadcrumbs, @@ -1482,6 +1483,56 @@ const EntityLineageComponent: FunctionComponent = ({ } }; + 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 * Change newly added node label based on entity:EntityReference @@ -1681,12 +1732,14 @@ const EntityLineageComponent: FunctionComponent = ({ (selectedEdgeInfo ? ( { setIsDrawerOpen(false); setSelectedEdgeInfo(undefined); }} + onEdgeDescriptionUpdate={onEdgeDescriptionUpdate} /> ) : ( { + const response = await APIClient.put(`/lineage`, edge); + + return response.data; +}; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/EntityLineageUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/EntityLineageUtils.tsx index 3c89ac7c697..ad02a7004f7 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/EntityLineageUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/EntityLineageUtils.tsx @@ -1372,3 +1372,16 @@ export const getSearchIndexFromNodeType = (entityType: string) => { 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 + ); +};