mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-09-04 14:43:11 +00:00
fix(ui): revamp lineage edge layout (#13612)
* revamp lineage edge layout * fix unit test
This commit is contained in:
parent
210a8f9d5c
commit
858d75f9c9
@ -1,98 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2022 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 { Button, Modal, Select } from 'antd';
|
|
||||||
import { t } from 'i18next';
|
|
||||||
import { isUndefined } from 'lodash';
|
|
||||||
import React from 'react';
|
|
||||||
import { EntityReference } from '../../../generated/entity/type';
|
|
||||||
import { getEntityName } from '../../../utils/EntityUtils';
|
|
||||||
|
|
||||||
interface AddPipeLineModalType {
|
|
||||||
showAddEdgeModal: boolean;
|
|
||||||
edgeSearchValue: string;
|
|
||||||
selectedEdgeId: string | undefined;
|
|
||||||
edgeOptions: EntityReference[];
|
|
||||||
onModalCancel: () => void;
|
|
||||||
onSave: () => void;
|
|
||||||
onClear: () => void;
|
|
||||||
onRemoveEdgeClick: (evt: React.MouseEvent<HTMLButtonElement>) => void;
|
|
||||||
onSearch: (value: string) => void;
|
|
||||||
onSelect: (value: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const AddPipeLineModal = ({
|
|
||||||
showAddEdgeModal,
|
|
||||||
edgeOptions,
|
|
||||||
edgeSearchValue,
|
|
||||||
selectedEdgeId,
|
|
||||||
onRemoveEdgeClick,
|
|
||||||
onModalCancel,
|
|
||||||
onSave,
|
|
||||||
onClear,
|
|
||||||
onSearch,
|
|
||||||
onSelect,
|
|
||||||
}: AddPipeLineModalType) => {
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
destroyOnClose
|
|
||||||
data-testid="add-edge-modal"
|
|
||||||
footer={[
|
|
||||||
<Button
|
|
||||||
data-testid="remove-edge-button"
|
|
||||||
key="remove-edge-btn"
|
|
||||||
type="text"
|
|
||||||
onClick={onRemoveEdgeClick}>
|
|
||||||
{t('label.remove-entity', {
|
|
||||||
entity: t('label.edge-lowercase'),
|
|
||||||
})}
|
|
||||||
</Button>,
|
|
||||||
<Button
|
|
||||||
data-testid="save-button"
|
|
||||||
key="save-btn"
|
|
||||||
type="primary"
|
|
||||||
onClick={onSave}>
|
|
||||||
{t('label.save')}
|
|
||||||
</Button>,
|
|
||||||
]}
|
|
||||||
maskClosable={false}
|
|
||||||
open={showAddEdgeModal}
|
|
||||||
title={t(`label.${isUndefined(selectedEdgeId) ? 'add' : 'edit'}-entity`, {
|
|
||||||
entity: t('label.edge'),
|
|
||||||
})}
|
|
||||||
onCancel={onModalCancel}>
|
|
||||||
<Select
|
|
||||||
allowClear
|
|
||||||
showSearch
|
|
||||||
className="w-full"
|
|
||||||
data-testid="field-select"
|
|
||||||
defaultActiveFirstOption={false}
|
|
||||||
filterOption={false}
|
|
||||||
notFoundContent={false}
|
|
||||||
options={edgeOptions.map((option) => ({
|
|
||||||
label: getEntityName(option),
|
|
||||||
value: option.id,
|
|
||||||
}))}
|
|
||||||
placeholder={t('message.search-for-edge')}
|
|
||||||
searchValue={edgeSearchValue}
|
|
||||||
showArrow={false}
|
|
||||||
value={selectedEdgeId}
|
|
||||||
onClear={onClear}
|
|
||||||
onSearch={onSearch}
|
|
||||||
onSelect={onSelect}
|
|
||||||
/>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AddPipeLineModal;
|
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2022 Collate.
|
* Copyright 2023 Collate.
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
@ -26,11 +26,11 @@ const mockProps = {
|
|||||||
name: 'Pipeline 1',
|
name: 'Pipeline 1',
|
||||||
id: 'test-pipeline-1',
|
id: 'test-pipeline-1',
|
||||||
type: 'pipeline',
|
type: 'pipeline',
|
||||||
|
fullyQualifiedName: 'sample_airflow/presto_etl',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
onModalCancel: jest.fn(),
|
onModalCancel: jest.fn(),
|
||||||
onSave: jest.fn(),
|
onSave: jest.fn(),
|
||||||
onClear: jest.fn(),
|
|
||||||
onRemoveEdgeClick: jest.fn(),
|
onRemoveEdgeClick: jest.fn(),
|
||||||
onSearch: jest.fn(),
|
onSearch: jest.fn(),
|
||||||
onSelect: jest.fn(),
|
onSelect: jest.fn(),
|
||||||
@ -41,7 +41,7 @@ describe('Test CustomEdge Component', () => {
|
|||||||
render(<AddPipeLineModal {...mockProps} />);
|
render(<AddPipeLineModal {...mockProps} />);
|
||||||
|
|
||||||
const edgeModal = await screen.findByTestId('add-edge-modal');
|
const edgeModal = await screen.findByTestId('add-edge-modal');
|
||||||
const fieldSelect = await screen.findByTestId('field-select');
|
const fieldSelect = await screen.findByTestId('field-input');
|
||||||
const removeEdge = await screen.findByTestId('remove-edge-button');
|
const removeEdge = await screen.findByTestId('remove-edge-button');
|
||||||
const saveButton = await screen.findByTestId('save-button');
|
const saveButton = await screen.findByTestId('save-button');
|
||||||
|
|
@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 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 { Button, Input, Modal } from 'antd';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { t } from 'i18next';
|
||||||
|
import { isEmpty, isUndefined } from 'lodash';
|
||||||
|
import React, { useMemo } from 'react';
|
||||||
|
import { ERROR_PLACEHOLDER_TYPE, SIZE } from '../../../../enums/common.enum';
|
||||||
|
import { EntityReference } from '../../../../generated/entity/type';
|
||||||
|
import { getEntityName } from '../../../../utils/EntityUtils';
|
||||||
|
import Fqn from '../../../../utils/Fqn';
|
||||||
|
import { getEntityIcon } from '../../../../utils/TableUtils';
|
||||||
|
import ErrorPlaceHolder from '../../../common/error-with-placeholder/ErrorPlaceHolder';
|
||||||
|
import '../../../FeedEditor/FeedEditor.css';
|
||||||
|
import './add-pipeline-modal.less';
|
||||||
|
|
||||||
|
interface AddPipeLineModalType {
|
||||||
|
showAddEdgeModal: boolean;
|
||||||
|
edgeSearchValue: string;
|
||||||
|
selectedEdgeId: string | undefined;
|
||||||
|
edgeOptions: EntityReference[];
|
||||||
|
onModalCancel: () => void;
|
||||||
|
onSave: () => void;
|
||||||
|
onRemoveEdgeClick: (evt: React.MouseEvent<HTMLButtonElement>) => void;
|
||||||
|
onSearch: (value: string) => void;
|
||||||
|
onSelect: (value: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AddPipeLineModal = ({
|
||||||
|
showAddEdgeModal,
|
||||||
|
edgeOptions,
|
||||||
|
edgeSearchValue,
|
||||||
|
selectedEdgeId,
|
||||||
|
onRemoveEdgeClick,
|
||||||
|
onModalCancel,
|
||||||
|
onSave,
|
||||||
|
onSearch,
|
||||||
|
onSelect,
|
||||||
|
}: AddPipeLineModalType) => {
|
||||||
|
const errorPlaceholderEdge = useMemo(() => {
|
||||||
|
if (isEmpty(edgeOptions)) {
|
||||||
|
if (edgeSearchValue) {
|
||||||
|
return (
|
||||||
|
<ErrorPlaceHolder
|
||||||
|
className="mt-0-important"
|
||||||
|
size={SIZE.MEDIUM}
|
||||||
|
type={ERROR_PLACEHOLDER_TYPE.FILTER}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <ErrorPlaceHolder />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}, [edgeOptions, edgeSearchValue]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
destroyOnClose
|
||||||
|
data-testid="add-edge-modal"
|
||||||
|
footer={[
|
||||||
|
<Button
|
||||||
|
data-testid="remove-edge-button"
|
||||||
|
key="remove-edge-btn"
|
||||||
|
type="text"
|
||||||
|
onClick={onRemoveEdgeClick}>
|
||||||
|
{t('label.remove-entity', {
|
||||||
|
entity: t('label.edge-lowercase'),
|
||||||
|
})}
|
||||||
|
</Button>,
|
||||||
|
<Button
|
||||||
|
data-testid="save-button"
|
||||||
|
key="save-btn"
|
||||||
|
type="primary"
|
||||||
|
onClick={onSave}>
|
||||||
|
{t('label.save')}
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
|
maskClosable={false}
|
||||||
|
open={showAddEdgeModal}
|
||||||
|
title={t(`label.${isUndefined(selectedEdgeId) ? 'add' : 'edit'}-entity`, {
|
||||||
|
entity: t('label.edge'),
|
||||||
|
})}
|
||||||
|
onCancel={onModalCancel}>
|
||||||
|
<Input
|
||||||
|
data-testid="field-input"
|
||||||
|
placeholder={t('message.search-for-edge')}
|
||||||
|
value={edgeSearchValue}
|
||||||
|
onChange={(e) => onSearch(e.target.value)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="edge-option-container">
|
||||||
|
{edgeOptions.map((item) => {
|
||||||
|
const icon = getEntityIcon(item.type);
|
||||||
|
const breadcrumb = Fqn.split(item.fullyQualifiedName).join('/');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classNames('edge-option-item gap-2', {
|
||||||
|
active: selectedEdgeId === item.id,
|
||||||
|
})}
|
||||||
|
key={item.id}
|
||||||
|
onClick={() => onSelect(item.id)}>
|
||||||
|
<div className="flex-center mention-icon-image">{icon}</div>
|
||||||
|
<div>
|
||||||
|
<div className="d-flex flex-wrap">
|
||||||
|
<span className="truncate breadcrumb">{breadcrumb}</span>
|
||||||
|
</div>
|
||||||
|
<div className="d-flex flex-col">
|
||||||
|
<span className="font-medium truncate">
|
||||||
|
{getEntityName(item)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
{errorPlaceholderEdge}
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AddPipeLineModal;
|
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 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 (reference) url('../../../../styles/variables.less');
|
||||||
|
|
||||||
|
.edge-option-container {
|
||||||
|
margin-top: 6px;
|
||||||
|
width: 100%;
|
||||||
|
height: 310px;
|
||||||
|
overflow: scroll;
|
||||||
|
|
||||||
|
.edge-option-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin: 8px 0;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: @primary-color-hover;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: @primary-color-hover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: @body-dark-bg-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb {
|
||||||
|
color: @text-grey-muted;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -118,7 +118,7 @@ import Loader from '../../Loader/Loader';
|
|||||||
import { useTourProvider } from '../../TourProvider/TourProvider';
|
import { useTourProvider } from '../../TourProvider/TourProvider';
|
||||||
import EdgeInfoDrawer from '../EntityInfoDrawer/EdgeInfoDrawer.component';
|
import EdgeInfoDrawer from '../EntityInfoDrawer/EdgeInfoDrawer.component';
|
||||||
import EntityInfoDrawer from '../EntityInfoDrawer/EntityInfoDrawer.component';
|
import EntityInfoDrawer from '../EntityInfoDrawer/EntityInfoDrawer.component';
|
||||||
import AddPipeLineModal from './AddPipeLineModal';
|
import AddPipeLineModal from './AppPipelineModel/AddPipeLineModal';
|
||||||
import CustomControlsComponent from './CustomControls.component';
|
import CustomControlsComponent from './CustomControls.component';
|
||||||
import './entity-lineage.style.less';
|
import './entity-lineage.style.less';
|
||||||
import {
|
import {
|
||||||
@ -398,6 +398,13 @@ const EntityLineageComponent: FunctionComponent<EntityLineageProp> = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleModalCancel = () => {
|
||||||
|
setSelectedEdgeId(undefined);
|
||||||
|
setShowAddEdgeModal(false);
|
||||||
|
setSelectedEdge({} as SelectedEdge);
|
||||||
|
setEdgeOptions([]);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param data selected edge
|
* @param data selected edge
|
||||||
@ -440,6 +447,7 @@ const EntityLineageComponent: FunctionComponent<EntityLineageProp> = ({
|
|||||||
|
|
||||||
resetSelectedData();
|
resetSelectedData();
|
||||||
setConfirmDelete(false);
|
setConfirmDelete(false);
|
||||||
|
handleModalCancel();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -999,18 +1007,6 @@ const EntityLineageComponent: FunctionComponent<EntityLineageProp> = ({
|
|||||||
setSelectedEdgeId(value);
|
setSelectedEdgeId(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleModalCancel = () => {
|
|
||||||
setSelectedEdgeId(undefined);
|
|
||||||
setShowAddEdgeModal(false);
|
|
||||||
setSelectedEdge({} as SelectedEdge);
|
|
||||||
setEdgeOptions([]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onEdgeSelectionClear = () => {
|
|
||||||
setSelectedEdgeId(undefined);
|
|
||||||
setEdgeSearchValue('');
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleModalSave = () => {
|
const handleModalSave = () => {
|
||||||
if (selectedEdge.data && updatedLineageData) {
|
if (selectedEdge.data && updatedLineageData) {
|
||||||
setStatus('waiting');
|
setStatus('waiting');
|
||||||
@ -1031,7 +1027,8 @@ const EntityLineageComponent: FunctionComponent<EntityLineageProp> = ({
|
|||||||
selectedEdgeValue,
|
selectedEdgeValue,
|
||||||
selectedEdgeId,
|
selectedEdgeId,
|
||||||
selectedEdge.data,
|
selectedEdge.data,
|
||||||
(edgeDetails?.type as EntityType) ?? EntityType.PIPELINE
|
(edgeDetails?.type as EntityType) ?? EntityType.PIPELINE,
|
||||||
|
edgeDetails
|
||||||
);
|
);
|
||||||
|
|
||||||
addLineageHandler(newEdge)
|
addLineageHandler(newEdge)
|
||||||
@ -1417,14 +1414,29 @@ const EntityLineageComponent: FunctionComponent<EntityLineageProp> = ({
|
|||||||
SearchIndex.PIPELINE,
|
SearchIndex.PIPELINE,
|
||||||
SearchIndex.STORED_PROCEDURE,
|
SearchIndex.STORED_PROCEDURE,
|
||||||
]);
|
]);
|
||||||
setEdgeOptions(
|
|
||||||
data.data.hits.hits.map((hit) =>
|
const selectedPipeline = selectedEdge.data?.pipeline;
|
||||||
getEntityReferenceFromEntity(
|
|
||||||
hit._source,
|
const edgeOptions = data.data.hits.hits.map((hit) =>
|
||||||
hit._source.entityType as EntityType
|
getEntityReferenceFromEntity(
|
||||||
)
|
hit._source,
|
||||||
|
hit._source.entityType as EntityType
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const optionContainItem = edgeOptions.find(
|
||||||
|
(item) => item.id === selectedEdgeId
|
||||||
|
);
|
||||||
|
|
||||||
|
setEdgeOptions([
|
||||||
|
...(selectedPipeline &&
|
||||||
|
isEmpty(optionContainItem) &&
|
||||||
|
isEmpty(edgeSearchValue) &&
|
||||||
|
selectedPipeline.id === selectedEdgeId
|
||||||
|
? [selectedPipeline]
|
||||||
|
: []),
|
||||||
|
...edgeOptions,
|
||||||
|
]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showErrorToast(
|
showErrorToast(
|
||||||
error as AxiosError,
|
error as AxiosError,
|
||||||
@ -1630,10 +1642,10 @@ const EntityLineageComponent: FunctionComponent<EntityLineageProp> = ({
|
|||||||
}, [selectedEdge, confirmDelete]);
|
}, [selectedEdge, confirmDelete]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (edgeSearchValue) {
|
if (showAddEdgeModal) {
|
||||||
getSearchResults(edgeSearchValue);
|
getSearchResults(edgeSearchValue);
|
||||||
}
|
}
|
||||||
}, [edgeSearchValue]);
|
}, [edgeSearchValue, showAddEdgeModal]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
edgesRef.current = edges;
|
edgesRef.current = edges;
|
||||||
@ -1775,7 +1787,6 @@ const EntityLineageComponent: FunctionComponent<EntityLineageProp> = ({
|
|||||||
edgeSearchValue={edgeSearchValue}
|
edgeSearchValue={edgeSearchValue}
|
||||||
selectedEdgeId={selectedEdgeId}
|
selectedEdgeId={selectedEdgeId}
|
||||||
showAddEdgeModal={showAddEdgeModal}
|
showAddEdgeModal={showAddEdgeModal}
|
||||||
onClear={onEdgeSelectionClear}
|
|
||||||
onModalCancel={handleModalCancel}
|
onModalCancel={handleModalCancel}
|
||||||
onRemoveEdgeClick={handleRemoveEdgeClick}
|
onRemoveEdgeClick={handleRemoveEdgeClick}
|
||||||
onSave={handleModalSave}
|
onSave={handleModalSave}
|
||||||
|
@ -410,10 +410,12 @@ const TagsContainerV2 = ({
|
|||||||
{header}
|
{header}
|
||||||
{tagBody}
|
{tagBody}
|
||||||
|
|
||||||
<Space align="baseline" className="m-t-xs w-full" size="middle">
|
{(children || showBottomEditButton) && (
|
||||||
{showBottomEditButton && !showInlineEditButton && editTagButton}
|
<Space align="baseline" className="m-t-xs w-full" size="middle">
|
||||||
{children}
|
{showBottomEditButton && !showInlineEditButton && editTagButton}
|
||||||
</Space>
|
{children}
|
||||||
|
</Space>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -824,7 +824,8 @@ export const getNewLineageConnectionDetails = (
|
|||||||
selectedEdgeValue: EntityLineageEdge | undefined,
|
selectedEdgeValue: EntityLineageEdge | undefined,
|
||||||
selectedPipelineId: string | undefined,
|
selectedPipelineId: string | undefined,
|
||||||
customEdgeData: CustomEdgeData,
|
customEdgeData: CustomEdgeData,
|
||||||
type: EntityType
|
type: EntityType,
|
||||||
|
edgeDetails?: EntityReference
|
||||||
) => {
|
) => {
|
||||||
const { source, sourceType, target, targetType } = customEdgeData;
|
const { source, sourceType, target, targetType } = customEdgeData;
|
||||||
const updatedLineageDetails: LineageDetails = {
|
const updatedLineageDetails: LineageDetails = {
|
||||||
@ -836,6 +837,7 @@ export const getNewLineageConnectionDetails = (
|
|||||||
: {
|
: {
|
||||||
id: selectedPipelineId,
|
id: selectedPipelineId,
|
||||||
type,
|
type,
|
||||||
|
fullyQualifiedName: edgeDetails?.fullyQualifiedName ?? '',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user