UI added support all deleted entities (#2388)

* Added deleted functionality for explore page

* Added UI support for deleted topics

* Added UI support for deleted Dashboards and Pipelines
This commit is contained in:
darth-coder00 2022-01-25 13:20:52 +05:30 committed by GitHub
parent b4d63bd951
commit e75e9d0780
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 360 additions and 192 deletions

View File

@ -58,7 +58,11 @@ export const getDashboardByFqn: Function = (
fqn: string, fqn: string,
arrQueryFields: string arrQueryFields: string
): Promise<AxiosResponse> => { ): Promise<AxiosResponse> => {
const url = getURLWithQueryFields(`/dashboards/name/${fqn}`, arrQueryFields); const url = getURLWithQueryFields(
`/dashboards/name/${fqn}`,
arrQueryFields,
'include=all'
);
return APIClient.get(url); return APIClient.get(url);
}; };

View File

@ -25,7 +25,8 @@ export const searchData: Function = (
filters: string, filters: string,
sortField: string, sortField: string,
sortOrder: string, sortOrder: string,
searchIndex: string searchIndex: string,
onlyDeleted = false
): Promise<AxiosResponse> => { ): Promise<AxiosResponse> => {
return APIClient.get( return APIClient.get(
`/search/query?${getSearchAPIQuery( `/search/query?${getSearchAPIQuery(
@ -35,7 +36,8 @@ export const searchData: Function = (
filters, filters,
sortField, sortField,
sortOrder, sortOrder,
searchIndex searchIndex,
onlyDeleted
)}` )}`
); );
}; };

View File

@ -58,7 +58,11 @@ export const getPipelineByFqn: Function = (
fqn: string, fqn: string,
arrQueryFields: string arrQueryFields: string
): Promise<AxiosResponse> => { ): Promise<AxiosResponse> => {
const url = getURLWithQueryFields(`/pipelines/name/${fqn}`, arrQueryFields); const url = getURLWithQueryFields(
`/pipelines/name/${fqn}`,
arrQueryFields,
'include=all'
);
return APIClient.get(url); return APIClient.get(url);
}; };

View File

@ -58,7 +58,11 @@ export const getTopicByFqn: Function = (
fqn: string, fqn: string,
arrQueryFields: string arrQueryFields: string
): Promise<AxiosResponse> => { ): Promise<AxiosResponse> => {
const url = getURLWithQueryFields(`/topics/name/${fqn}`, arrQueryFields); const url = getURLWithQueryFields(
`/topics/name/${fqn}`,
arrQueryFields,
'include=all'
);
return APIClient.get(url); return APIClient.get(url);
}; };

View File

@ -71,6 +71,7 @@ const DashboardDetails = ({
loadNodeHandler, loadNodeHandler,
versionHandler, versionHandler,
version, version,
deleted,
addLineageHandler, addLineageHandler,
removeLineageHandler, removeLineageHandler,
}: DashboardDetailsProps) => { }: DashboardDetailsProps) => {
@ -131,6 +132,7 @@ const DashboardDetails = ({
selectedName: 'icon-managecolor', selectedName: 'icon-managecolor',
}, },
isProtected: true, isProtected: true,
isHidden: deleted,
protectedState: !owner || hasEditAccess(), protectedState: !owner || hasEditAccess(),
position: 3, position: 3,
}, },
@ -318,6 +320,7 @@ const DashboardDetails = ({
<div className="tw-px-6 tw-w-full tw-h-full tw-flex tw-flex-col"> <div className="tw-px-6 tw-w-full tw-h-full tw-flex tw-flex-col">
<EntityPageInfo <EntityPageInfo
isTagEditable isTagEditable
deleted={deleted}
entityName={entityName} entityName={entityName}
extraInfo={extraInfo} extraInfo={extraInfo}
followHandler={followDashboard} followHandler={followDashboard}
@ -352,6 +355,7 @@ const DashboardDetails = ({
entityName={entityName} entityName={entityName}
hasEditAccess={hasEditAccess()} hasEditAccess={hasEditAccess()}
isEdit={isEdit} isEdit={isEdit}
isReadOnly={deleted}
owner={owner} owner={owner}
onCancel={onCancel} onCancel={onCancel}
onDescriptionEdit={onDescriptionEdit} onDescriptionEdit={onDescriptionEdit}
@ -413,25 +417,27 @@ const DashboardDetails = ({
</span> </span>
)} )}
</div> </div>
<NonAdminAction {!deleted && (
html={getHtmlForNonAdminAction( <NonAdminAction
Boolean(owner) html={getHtmlForNonAdminAction(
)} Boolean(owner)
isOwner={hasEditAccess()} )}
position="top"> isOwner={hasEditAccess()}
<button position="top">
className="tw-self-start tw-w-8 tw-h-auto tw-opacity-0 tw-ml-1 group-hover:tw-opacity-100 focus:tw-outline-none" <button
onClick={() => className="tw-self-start tw-w-8 tw-h-auto tw-opacity-0 tw-ml-1 group-hover:tw-opacity-100 focus:tw-outline-none"
handleUpdateChart(chart, index) onClick={() =>
}> handleUpdateChart(chart, index)
<SVGIcons }>
alt="edit" <SVGIcons
icon="icon-edit" alt="edit"
title="Edit" icon="icon-edit"
width="10px" title="Edit"
/> width="10px"
</button> />
</NonAdminAction> </button>
</NonAdminAction>
)}
</div> </div>
</div> </div>
</td> </td>
@ -442,44 +448,61 @@ const DashboardDetails = ({
handleEditChartTag(chart, index); handleEditChartTag(chart, index);
} }
}}> }}>
<NonAdminAction {deleted ? (
html={getHtmlForNonAdminAction(Boolean(owner))} <div className="tw-flex tw-flex-wrap">
isOwner={hasEditAccess()} {chart.tags?.map(
position="left" (tag: TagLabel, i: number) => (
trigger="click">
<TagsContainer
editable={editChartTags?.index === index}
selectedTags={chart.tags as EntityTags[]}
tagList={tagList}
type="label"
onCancel={() => {
handleChartTagSelection();
}}
onSelectionChange={(tags) => {
handleChartTagSelection(tags);
}}>
{chart.tags?.length ? (
<button
className="tw-opacity-0 tw-ml-1 group-hover:tw-opacity-100 focus:tw-outline-none"
data-testid="edit-tags">
<SVGIcons
alt="edit"
icon="icon-edit"
title="Edit"
width="10px"
/>
</button>
) : (
<span className="tw-opacity-60 group-hover:tw-opacity-100 tw-text-grey-muted group-hover:tw-text-primary">
<Tags <Tags
startWith="+ " key={i}
tag="Add tag" startWith="#"
type="outlined" tag={tag}
type="label"
/> />
</span> )
)} )}
</TagsContainer> </div>
</NonAdminAction> ) : (
<NonAdminAction
html={getHtmlForNonAdminAction(
Boolean(owner)
)}
isOwner={hasEditAccess()}
position="left"
trigger="click">
<TagsContainer
editable={editChartTags?.index === index}
selectedTags={chart.tags as EntityTags[]}
tagList={tagList}
type="label"
onCancel={() => {
handleChartTagSelection();
}}
onSelectionChange={(tags) => {
handleChartTagSelection(tags);
}}>
{chart.tags?.length ? (
<button
className="tw-opacity-0 tw-ml-1 group-hover:tw-opacity-100 focus:tw-outline-none"
data-testid="edit-tags">
<SVGIcons
alt="edit"
icon="icon-edit"
title="Edit"
width="10px"
/>
</button>
) : (
<span className="tw-opacity-60 group-hover:tw-opacity-100 tw-text-grey-muted group-hover:tw-text-primary">
<Tags
startWith="+ "
tag="Add tag"
type="outlined"
/>
</span>
)}
</TagsContainer>
</NonAdminAction>
)}
</td> </td>
</tr> </tr>
))} ))}
@ -492,6 +515,7 @@ const DashboardDetails = ({
<div className="tw-h-full"> <div className="tw-h-full">
<Entitylineage <Entitylineage
addLineageHandler={addLineageHandler} addLineageHandler={addLineageHandler}
deleted={deleted}
entityLineage={entityLineage} entityLineage={entityLineage}
isNodeLoading={isNodeLoading} isNodeLoading={isNodeLoading}
lineageLeafNodes={lineageLeafNodes} lineageLeafNodes={lineageLeafNodes}
@ -500,7 +524,7 @@ const DashboardDetails = ({
/> />
</div> </div>
)} )}
{activeTab === 3 && ( {activeTab === 3 && !deleted && (
<div> <div>
<ManageTabComponent <ManageTabComponent
currentTier={tier?.tagFQN} currentTier={tier?.tagFQN}

View File

@ -51,6 +51,7 @@ export interface DashboardDetailsProps {
followers: Array<User>; followers: Array<User>;
dashboardTags: Array<EntityTags>; dashboardTags: Array<EntityTags>;
slashedDashboardName: TitleBreadcrumbProps['titleLinks']; slashedDashboardName: TitleBreadcrumbProps['titleLinks'];
deleted?: boolean;
setActiveTabHandler: (value: number) => void; setActiveTabHandler: (value: number) => void;
followDashboardHandler: () => void; followDashboardHandler: () => void;
unfollowDashboardHandler: () => void; unfollowDashboardHandler: () => void;

View File

@ -483,6 +483,7 @@ const DatasetDetails: React.FC<DatasetDetailsProps> = ({
id="lineageDetails"> id="lineageDetails">
<Entitylineage <Entitylineage
addLineageHandler={addLineageHandler} addLineageHandler={addLineageHandler}
deleted={deleted}
entityLineage={entityLineage} entityLineage={entityLineage}
isNodeLoading={isNodeLoading} isNodeLoading={isNodeLoading}
lineageLeafNodes={lineageLeafNodes} lineageLeafNodes={lineageLeafNodes}

View File

@ -77,6 +77,7 @@ const Entitylineage: FunctionComponent<EntityLineageProp> = ({
loadNodeHandler, loadNodeHandler,
lineageLeafNodes, lineageLeafNodes,
isNodeLoading, isNodeLoading,
deleted,
addLineageHandler, addLineageHandler,
removeLineageHandler, removeLineageHandler,
}: EntityLineageProp) => { }: EntityLineageProp) => {
@ -585,41 +586,43 @@ const Entitylineage: FunctionComponent<EntityLineageProp> = ({
<CustomControls <CustomControls
className="tw-absolute tw-top-1 tw-right-1 tw-bottom-full tw-ml-4 tw-mt-4" className="tw-absolute tw-top-1 tw-right-1 tw-bottom-full tw-ml-4 tw-mt-4"
fitViewParams={{ minZoom: 0.5, maxZoom: 2.5 }}> fitViewParams={{ minZoom: 0.5, maxZoom: 2.5 }}>
<ControlButton {!deleted && (
className={classNames( <ControlButton
'tw-h-9 tw-w-9 tw-rounded-full tw-px-1 tw-shadow-lg tw-cursor-pointer', className={classNames(
{ 'tw-h-9 tw-w-9 tw-rounded-full tw-px-1 tw-shadow-lg tw-cursor-pointer',
'tw-bg-primary': isEditMode, {
'tw-bg-primary-hover-lite': !isEditMode, 'tw-bg-primary': isEditMode,
} 'tw-bg-primary-hover-lite': !isEditMode,
)}
onClick={() => {
setEditMode((pre) => !pre);
setSelectedNode({} as SelectedNode);
setIsDrawerOpen(false);
setNewAddedNode({} as FlowElement);
}}>
{loading ? (
<Loader size="small" type="white" />
) : status === 'success' ? (
<i
aria-hidden="true"
className="fa fa-check tw-text-white"
/>
) : (
<SVGIcons
alt="icon-edit-lineag"
className="tw--mt-1"
data-testid="edit-lineage"
icon={
!isEditMode
? 'icon-edit-lineage-color'
: 'icon-edit-lineage'
} }
width="14" )}
/> onClick={() => {
)} setEditMode((pre) => !pre && !deleted);
</ControlButton> setSelectedNode({} as SelectedNode);
setIsDrawerOpen(false);
setNewAddedNode({} as FlowElement);
}}>
{loading ? (
<Loader size="small" type="white" />
) : status === 'success' ? (
<i
aria-hidden="true"
className="fa fa-check tw-text-white"
/>
) : (
<SVGIcons
alt="icon-edit-lineag"
className="tw--mt-1"
data-testid="edit-lineage"
icon={
!isEditMode
? 'icon-edit-lineage-color'
: 'icon-edit-lineage'
}
width="14"
/>
)}
</ControlButton>
)}
</CustomControls> </CustomControls>
{isEditMode ? ( {isEditMode ? (
<Background <Background

View File

@ -28,6 +28,7 @@ export interface EntityLineageProp {
isNodeLoading: LoadingNodeState; isNodeLoading: LoadingNodeState;
lineageLeafNodes: LeafNodes; lineageLeafNodes: LeafNodes;
entityLineage: EntityLineage; entityLineage: EntityLineage;
deleted?: boolean;
loadNodeHandler: (node: EntityReference, pos: LineagePos) => void; loadNodeHandler: (node: EntityReference, pos: LineagePos) => void;
addLineageHandler: (edge: Edge) => Promise<void>; addLineageHandler: (edge: Edge) => Promise<void>;
removeLineageHandler: (data: EdgeData) => void; removeLineageHandler: (data: EdgeData) => void;

View File

@ -74,6 +74,8 @@ const Explore: React.FC<ExploreProps> = ({
handlePathChange, handlePathChange,
handleSearchText, handleSearchText,
fetchData, fetchData,
showDeleted,
onShowDeleted,
updateTableCount, updateTableCount,
updateTopicCount, updateTopicCount,
updateDashboardCount, updateDashboardCount,
@ -145,6 +147,10 @@ const Explore: React.FC<ExploreProps> = ({
} }
}; };
const handleShowDeleted = (checked: boolean) => {
onShowDeleted(checked);
};
const onClearFilterHandler = (type: string[]) => { const onClearFilterHandler = (type: string[]) => {
setFilters((prevFilters) => { setFilters((prevFilters) => {
const updatedFilter = type.reduce((filterObj, type) => { const updatedFilter = type.reduce((filterObj, type) => {
@ -482,7 +488,7 @@ const Explore: React.FC<ExploreProps> = ({
resetFilters(); resetFilters();
fetchTableData(); fetchTableData();
} }
}, [searchText, searchIndex]); }, [searchText, searchIndex, showDeleted]);
useEffect(() => { useEffect(() => {
if (searchResult) { if (searchResult) {
@ -549,6 +555,8 @@ const Explore: React.FC<ExploreProps> = ({
<FacetFilter <FacetFilter
aggregations={getAggrWithDefaultValue(aggregations, visibleFilters)} aggregations={getAggrWithDefaultValue(aggregations, visibleFilters)}
filters={getFacetedFilter()} filters={getFacetedFilter()}
showDeletedOnly={showDeleted}
onSelectDeleted={handleShowDeleted}
onSelectHandler={handleSelectedFilter} onSelectHandler={handleSelectedFilter}
/> />
)} )}

View File

@ -58,6 +58,7 @@ describe('Test Explore component', () => {
searchQuery="" searchQuery=""
searchResult={mockSearchResult} searchResult={mockSearchResult}
searchText="" searchText=""
showDeleted={false}
sortValue="" sortValue=""
tab="" tab=""
tabCounts={{ tabCounts={{
@ -72,6 +73,7 @@ describe('Test Explore component', () => {
updatePipelineCount={mockFunction} updatePipelineCount={mockFunction}
updateTableCount={mockFunction} updateTableCount={mockFunction}
updateTopicCount={mockFunction} updateTopicCount={mockFunction}
onShowDeleted={mockFunction}
/>, />,
{ {
wrapper: MemoryRouter, wrapper: MemoryRouter,

View File

@ -39,6 +39,7 @@ export interface ExploreProps {
tab: string; tab: string;
error: string; error: string;
searchQuery: string; searchQuery: string;
showDeleted: boolean;
fetchCount: () => void; fetchCount: () => void;
handlePathChange: (path: string) => void; handlePathChange: (path: string) => void;
handleSearchText: (text: string) => void; handleSearchText: (text: string) => void;
@ -48,5 +49,6 @@ export interface ExploreProps {
updatePipelineCount: (count: number) => void; updatePipelineCount: (count: number) => void;
updateDbtModelCount: (count: number) => void; updateDbtModelCount: (count: number) => void;
fetchData: (value: SearchDataFunctionType[]) => void; fetchData: (value: SearchDataFunctionType[]) => void;
onShowDeleted: (checked: boolean) => void;
searchResult: ExploreSearchData | undefined; searchResult: ExploreSearchData | undefined;
} }

View File

@ -23,6 +23,7 @@ import { LabelType, State } from '../../generated/type/tagLabel';
import { useAuth } from '../../hooks/authHooks'; import { useAuth } from '../../hooks/authHooks';
import { import {
getCurrentUserId, getCurrentUserId,
getHtmlForNonAdminAction,
getUserTeams, getUserTeams,
isEven, isEven,
} from '../../utils/CommonUtils'; } from '../../utils/CommonUtils';
@ -30,6 +31,7 @@ import SVGIcons from '../../utils/SvgUtils';
import { getTagsWithoutTier } from '../../utils/TableUtils'; import { getTagsWithoutTier } from '../../utils/TableUtils';
import Description from '../common/description/Description'; import Description from '../common/description/Description';
import EntityPageInfo from '../common/entityPageInfo/EntityPageInfo'; import EntityPageInfo from '../common/entityPageInfo/EntityPageInfo';
import NonAdminAction from '../common/non-admin-action/NonAdminAction';
import RichTextEditorPreviewer from '../common/rich-text-editor/RichTextEditorPreviewer'; import RichTextEditorPreviewer from '../common/rich-text-editor/RichTextEditorPreviewer';
import TabsPane from '../common/TabsPane/TabsPane'; import TabsPane from '../common/TabsPane/TabsPane';
import PageContainer from '../containers/PageContainer'; import PageContainer from '../containers/PageContainer';
@ -65,6 +67,7 @@ const PipelineDetails = ({
lineageLeafNodes, lineageLeafNodes,
isNodeLoading, isNodeLoading,
version, version,
deleted,
versionHandler, versionHandler,
addLineageHandler, addLineageHandler,
removeLineageHandler, removeLineageHandler,
@ -123,6 +126,7 @@ const PipelineDetails = ({
selectedName: 'icon-managecolor', selectedName: 'icon-managecolor',
}, },
isProtected: true, isProtected: true,
isHidden: deleted,
protectedState: !owner || hasEditAccess(), protectedState: !owner || hasEditAccess(),
position: 3, position: 3,
}, },
@ -274,6 +278,7 @@ const PipelineDetails = ({
<div className="tw-px-6 tw-w-full tw-h-full tw-flex tw-flex-col"> <div className="tw-px-6 tw-w-full tw-h-full tw-flex tw-flex-col">
<EntityPageInfo <EntityPageInfo
isTagEditable isTagEditable
deleted={deleted}
entityName={entityName} entityName={entityName}
extraInfo={extraInfo} extraInfo={extraInfo}
followHandler={followPipeline} followHandler={followPipeline}
@ -307,6 +312,7 @@ const PipelineDetails = ({
entityName={entityName} entityName={entityName}
hasEditAccess={hasEditAccess()} hasEditAccess={hasEditAccess()}
isEdit={isEdit} isEdit={isEdit}
isReadOnly={deleted}
owner={owner} owner={owner}
onCancel={onCancel} onCancel={onCancel}
onDescriptionEdit={onDescriptionEdit} onDescriptionEdit={onDescriptionEdit}
@ -363,16 +369,27 @@ const PipelineDetails = ({
</span> </span>
)} )}
</div> </div>
<button {!deleted && (
className="tw-self-start tw-w-8 tw-h-auto tw-opacity-0 tw-ml-1 group-hover:tw-opacity-100 focus:tw-outline-none" <NonAdminAction
onClick={() => handleUpdateTask(task, index)}> html={getHtmlForNonAdminAction(
<SVGIcons Boolean(owner)
alt="edit" )}
icon="icon-edit" isOwner={hasEditAccess()}
title="Edit" position="top">
width="10px" <button
/> className="tw-self-start tw-w-8 tw-h-auto tw-opacity-0 tw-ml-1 group-hover:tw-opacity-100 focus:tw-outline-none"
</button> onClick={() =>
handleUpdateTask(task, index)
}>
<SVGIcons
alt="edit"
icon="icon-edit"
title="Edit"
width="10px"
/>
</button>
</NonAdminAction>
)}
</div> </div>
</td> </td>
<td className="tableBody-cell">{task.taskType}</td> <td className="tableBody-cell">{task.taskType}</td>
@ -387,6 +404,7 @@ const PipelineDetails = ({
<div className="tw-h-full"> <div className="tw-h-full">
<Entitylineage <Entitylineage
addLineageHandler={addLineageHandler} addLineageHandler={addLineageHandler}
deleted={deleted}
entityLineage={entityLineage} entityLineage={entityLineage}
isNodeLoading={isNodeLoading} isNodeLoading={isNodeLoading}
lineageLeafNodes={lineageLeafNodes} lineageLeafNodes={lineageLeafNodes}
@ -395,7 +413,7 @@ const PipelineDetails = ({
/> />
</div> </div>
)} )}
{activeTab === 3 && ( {activeTab === 3 && !deleted && (
<div> <div>
<ManageTabComponent <ManageTabComponent
currentTier={tier?.tagFQN} currentTier={tier?.tagFQN}

View File

@ -48,6 +48,7 @@ export interface PipeLineDetailsProp {
slashedPipelineName: TitleBreadcrumbProps['titleLinks']; slashedPipelineName: TitleBreadcrumbProps['titleLinks'];
entityLineage: EntityLineage; entityLineage: EntityLineage;
tasks: Task[]; tasks: Task[];
deleted?: boolean;
setActiveTabHandler: (value: number) => void; setActiveTabHandler: (value: number) => void;
followPipelineHandler: () => void; followPipelineHandler: () => void;
unfollowPipelineHandler: () => void; unfollowPipelineHandler: () => void;

View File

@ -56,6 +56,7 @@ const TopicDetails: React.FC<TopicDetailsProps> = ({
tagUpdateHandler, tagUpdateHandler,
version, version,
versionHandler, versionHandler,
deleted,
}: TopicDetailsProps) => { }: TopicDetailsProps) => {
const { isAuthDisabled } = useAuth(); const { isAuthDisabled } = useAuth();
const [isEdit, setIsEdit] = useState(false); const [isEdit, setIsEdit] = useState(false);
@ -139,6 +140,7 @@ const TopicDetails: React.FC<TopicDetailsProps> = ({
selectedName: 'icon-managecolor', selectedName: 'icon-managecolor',
}, },
isProtected: true, isProtected: true,
isHidden: deleted,
protectedState: !owner || hasEditAccess(), protectedState: !owner || hasEditAccess(),
position: 3, position: 3,
}, },
@ -275,6 +277,7 @@ const TopicDetails: React.FC<TopicDetailsProps> = ({
<div className="tw-px-6 tw-w-full tw-h-full tw-flex tw-flex-col"> <div className="tw-px-6 tw-w-full tw-h-full tw-flex tw-flex-col">
<EntityPageInfo <EntityPageInfo
isTagEditable isTagEditable
deleted={deleted}
entityName={entityName} entityName={entityName}
extraInfo={extraInfo} extraInfo={extraInfo}
followHandler={followTopic} followHandler={followTopic}
@ -308,6 +311,7 @@ const TopicDetails: React.FC<TopicDetailsProps> = ({
entityName={entityName} entityName={entityName}
hasEditAccess={hasEditAccess()} hasEditAccess={hasEditAccess()}
isEdit={isEdit} isEdit={isEdit}
isReadOnly={deleted}
owner={owner} owner={owner}
onCancel={onCancel} onCancel={onCancel}
onDescriptionEdit={onDescriptionEdit} onDescriptionEdit={onDescriptionEdit}
@ -326,7 +330,7 @@ const TopicDetails: React.FC<TopicDetailsProps> = ({
<SchemaEditor value={JSON.stringify(getConfigObject())} /> <SchemaEditor value={JSON.stringify(getConfigObject())} />
</div> </div>
)} )}
{activeTab === 3 && ( {activeTab === 3 && !deleted && (
<div> <div>
<ManageTabComponent <ManageTabComponent
currentTier={tier?.tagFQN} currentTier={tier?.tagFQN}

View File

@ -37,6 +37,7 @@ export interface TopicDetailsProps {
followers: Array<User>; followers: Array<User>;
topicTags: Array<EntityTags>; topicTags: Array<EntityTags>;
slashedTopicName: TitleBreadcrumbProps['titleLinks']; slashedTopicName: TitleBreadcrumbProps['titleLinks'];
deleted?: boolean;
setActiveTabHandler: (value: number) => void; setActiveTabHandler: (value: number) => void;
followTopicHandler: () => void; followTopicHandler: () => void;
unfollowTopicHandler: () => void; unfollowTopicHandler: () => void;

View File

@ -284,7 +284,7 @@ const EntityPageInfo = ({
))} ))}
</div> </div>
<div className="tw-flex tw-flex-wrap tw-pt-1 tw-ml-7 tw-group"> <div className="tw-flex tw-flex-wrap tw-pt-1 tw-ml-7 tw-group">
{(!isEditable || !isTagEditable) && ( {(!isEditable || !isTagEditable || deleted) && (
<> <>
{(tags.length > 0 || !isEmpty(tier)) && ( {(tags.length > 0 || !isEmpty(tier)) && (
<SVGIcons <SVGIcons
@ -347,7 +347,7 @@ const EntityPageInfo = ({
)} )}
</> </>
)} )}
{isTagEditable && ( {isTagEditable && !deleted && (
<NonAdminAction <NonAdminAction
html={getHtmlForNonAdminAction(Boolean(owner))} html={getHtmlForNonAdminAction(Boolean(owner))}
isOwner={hasEditAccess} isOwner={hasEditAccess}

View File

@ -11,6 +11,7 @@
* limitations under the License. * limitations under the License.
*/ */
import classNames from 'classnames';
import { lowerCase } from 'lodash'; import { lowerCase } from 'lodash';
import { AggregationType, Bucket, FilterObject } from 'Models'; import { AggregationType, Bucket, FilterObject } from 'Models';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
@ -23,8 +24,10 @@ import { FacetProp } from './FacetTypes';
import FilterContainer from './FilterContainer'; import FilterContainer from './FilterContainer';
const FacetFilter: FunctionComponent<FacetProp> = ({ const FacetFilter: FunctionComponent<FacetProp> = ({
aggregations, aggregations,
onSelectHandler,
filters, filters,
showDeletedOnly = false,
onSelectHandler,
onSelectDeleted,
onClearFilter, onClearFilter,
onSelectAllFilter, onSelectAllFilter,
}: FacetProp) => { }: FacetProp) => {
@ -133,6 +136,31 @@ const FacetFilter: FunctionComponent<FacetProp> = ({
return ( return (
<> <>
<div
className="sidebar-my-data-holder mt-2 mb-3"
data-testid="show-deleted-cntnr">
<div
className="filter-group tw-justify-between tw-mb-2"
data-testid="filter-container-deleted">
<div className="tw-flex">
<div className="filters-title tw-w-40 tw-truncate custom-checkbox-label">
Show Deleted
</div>
</div>
<div
className={classNames(
'toggle-switch tw-mr-0',
showDeletedOnly ? 'open' : null
)}
data-testid="show-deleted"
onClick={() => {
onSelectDeleted?.(!showDeletedOnly);
}}>
<div className="switch" />
</div>
</div>
</div>
{getSeparator(aggregations.length, 0)}
{aggregations.map((aggregation: AggregationType, index: number) => { {aggregations.map((aggregation: AggregationType, index: number) => {
return ( return (
<Fragment key={index}> <Fragment key={index}>

View File

@ -15,24 +15,26 @@ import { AggregationType, FilterObject } from 'Models';
export type FacetProp = { export type FacetProp = {
aggregations: Array<AggregationType>; aggregations: Array<AggregationType>;
filters: FilterObject;
showDeletedOnly?: boolean;
onSelectHandler: ( onSelectHandler: (
checked: boolean, checked: boolean,
name: string, name: string,
type: keyof FilterObject type: keyof FilterObject
) => void; ) => void;
filters: FilterObject;
onClearFilter?: (value: keyof FilterObject) => void; onClearFilter?: (value: keyof FilterObject) => void;
onSelectAllFilter?: ( onSelectAllFilter?: (
type: keyof FilterObject, type: keyof FilterObject,
filters: Array<string> filters: Array<string>
) => void; ) => void;
onSelectDeleted?: (checked: boolean) => void;
}; };
export type FilterContainerProp = { export type FilterContainerProp = {
name: string; name: string;
count: number; count?: number;
onSelect: (checked: boolean, name: string, type: keyof FilterObject) => void; onSelect: (checked: boolean, name: string, type: keyof FilterObject) => void;
isSelected: boolean; isSelected: boolean;
type: keyof FilterObject; type?: keyof FilterObject;
isDisabled?: boolean; isDisabled?: boolean;
}; };

View File

@ -12,6 +12,7 @@
*/ */
import classNames from 'classnames'; import classNames from 'classnames';
import { isNil } from 'lodash';
import React, { FunctionComponent } from 'react'; import React, { FunctionComponent } from 'react';
import { getCountBadge } from '../../../utils/CommonUtils'; import { getCountBadge } from '../../../utils/CommonUtils';
import { FilterContainerProp } from './FacetTypes'; import { FilterContainerProp } from './FacetTypes';
@ -20,7 +21,7 @@ const FilterContainer: FunctionComponent<FilterContainerProp> = ({
count, count,
onSelect, onSelect,
isSelected, isSelected,
type, type = '',
isDisabled = false, isDisabled = false,
}: FilterContainerProp) => { }: FilterContainerProp) => {
return ( return (
@ -49,7 +50,8 @@ const FilterContainer: FunctionComponent<FilterContainerProp> = ({
{name.startsWith('Tier.Tier') ? name.split('.')[1] : name} {name.startsWith('Tier.Tier') ? name.split('.')[1] : name}
</div> </div>
</div> </div>
{getCountBadge(count, classNames('tw-py-0 tw-px-0'), isSelected)} {!isNil(count) &&
getCountBadge(count, classNames('tw-py-0 tw-px-0'), isSelected)}
</div> </div>
); );
}; };

View File

@ -11,7 +11,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { AxiosPromise, AxiosResponse } from 'axios'; import { AxiosError, AxiosPromise, AxiosResponse } from 'axios';
import { compare, Operation } from 'fast-json-patch'; import { compare, Operation } from 'fast-json-patch';
import { import {
EntityTags, EntityTags,
@ -106,6 +106,7 @@ const DashboardDetailsPage = () => {
state: false, state: false,
}); });
const [currentVersion, setCurrentVersion] = useState<string>(); const [currentVersion, setCurrentVersion] = useState<string>();
const [deleted, setDeleted] = useState<boolean>(false);
const activeTabHandler = (tabValue: number) => { const activeTabHandler = (tabValue: number) => {
const currentTabIndex = tabValue - 1; const currentTabIndex = tabValue - 1;
@ -195,6 +196,22 @@ const DashboardDetailsPage = () => {
}); });
}; };
const getLineageData = () => {
getLineageByFQN(dashboardFQN, EntityType.DASHBOARD)
.then((res: AxiosResponse) => {
setEntityLineage(res.data);
})
.catch((err: AxiosError) => {
showToast({
variant: 'error',
body: err.message ?? 'Error while fetching lineage data',
});
})
.finally(() => {
setIsLineageLoading(false);
});
};
const fetchDashboardDetail = (dashboardFQN: string) => { const fetchDashboardDetail = (dashboardFQN: string) => {
setLoading(true); setLoading(true);
getDashboardByFqn(dashboardFQN, [ getDashboardByFqn(dashboardFQN, [
@ -203,69 +220,72 @@ const DashboardDetailsPage = () => {
'tags', 'tags',
'usageSummary', 'usageSummary',
'charts', 'charts',
]).then((res: AxiosResponse) => { ])
const { .then((res: AxiosResponse) => {
id, const {
description, id,
followers, deleted,
fullyQualifiedName, description,
service, followers,
tags, fullyQualifiedName,
owner, service,
displayName, tags,
charts, owner,
dashboardUrl, displayName,
serviceType, charts,
version, dashboardUrl,
} = res.data; serviceType,
setDisplayName(displayName); version,
setDashboardDetails(res.data); } = res.data;
setCurrentVersion(version); setDisplayName(displayName);
setDashboardId(id); setDashboardDetails(res.data);
setDescription(description ?? ''); setCurrentVersion(version);
setFollowers(followers); setDashboardId(id);
setOwner(getOwnerFromId(owner?.id)); setDescription(description ?? '');
setTier(getTierTags(tags)); setFollowers(followers);
setTags(getTagsWithoutTier(tags)); setOwner(getOwnerFromId(owner?.id));
setServiceType(serviceType); setTier(getTierTags(tags));
setSlashedDashboardName([ setTags(getTagsWithoutTier(tags));
{ setServiceType(serviceType);
name: service.name, setDeleted(deleted);
url: service.name setSlashedDashboardName([
? getServiceDetailsPath( {
service.name, name: service.name,
serviceType, url: service.name
ServiceCategory.DASHBOARD_SERVICES ? getServiceDetailsPath(
) service.name,
: '', serviceType,
imgSrc: serviceType ? serviceTypeLogo(serviceType) : undefined, ServiceCategory.DASHBOARD_SERVICES
}, )
{ : '',
name: displayName, imgSrc: serviceType ? serviceTypeLogo(serviceType) : undefined,
url: '', },
activeTitle: true, {
}, name: displayName,
]); url: '',
activeTitle: true,
},
]);
addToRecentViewed({ addToRecentViewed({
entityType: EntityType.DASHBOARD, entityType: EntityType.DASHBOARD,
fqn: fullyQualifiedName, fqn: fullyQualifiedName,
serviceType: serviceType, serviceType: serviceType,
timestamp: 0, timestamp: 0,
});
getLineageByFQN(dashboardFQN, EntityType.DASHBOARD)
.then((res: AxiosResponse) => {
setEntityLineage(res.data);
})
.finally(() => {
setIsLineageLoading(false);
}); });
setDashboardUrl(dashboardUrl); if (!deleted) {
fetchCharts(charts).then((charts) => setCharts(charts)); getLineageData();
setLoading(false); } else {
}); setIsLineageLoading(false);
}
setDashboardUrl(dashboardUrl);
fetchCharts(charts).then((charts) => setCharts(charts));
})
.finally(() => {
setLoading(false);
});
}; };
const descriptionUpdateHandler = (updatedDashboard: Dashboard) => { const descriptionUpdateHandler = (updatedDashboard: Dashboard) => {
@ -432,6 +452,7 @@ const DashboardDetailsPage = () => {
dashboardDetails={dashboardDetails} dashboardDetails={dashboardDetails}
dashboardTags={tags} dashboardTags={tags}
dashboardUrl={dashboardUrl} dashboardUrl={dashboardUrl}
deleted={deleted}
description={description} description={description}
descriptionUpdateHandler={descriptionUpdateHandler} descriptionUpdateHandler={descriptionUpdateHandler}
entityLineage={entityLineage} entityLineage={entityLineage}

View File

@ -11,7 +11,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { AxiosResponse } from 'axios'; import { AxiosError, AxiosResponse } from 'axios';
import { compare, Operation } from 'fast-json-patch'; import { compare, Operation } from 'fast-json-patch';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { import {
@ -107,6 +107,7 @@ const PipelineDetailsPage = () => {
const [leafNodes, setLeafNodes] = useState<LeafNodes>({} as LeafNodes); const [leafNodes, setLeafNodes] = useState<LeafNodes>({} as LeafNodes);
const [currentVersion, setCurrentVersion] = useState<string>(); const [currentVersion, setCurrentVersion] = useState<string>();
const [deleted, setDeleted] = useState<boolean>(false);
const activeTabHandler = (tabValue: number) => { const activeTabHandler = (tabValue: number) => {
const currentTabIndex = tabValue - 1; const currentTabIndex = tabValue - 1;
@ -148,12 +149,29 @@ const PipelineDetailsPage = () => {
}); });
}; };
const getLineageData = () => {
getLineageByFQN(pipelineFQN, EntityType.PIPELINE)
.then((res: AxiosResponse) => {
setEntityLineage(res.data);
})
.catch((err: AxiosError) => {
showToast({
variant: 'error',
body: err.message ?? 'Error while fetching lineage data',
});
})
.finally(() => {
setIsLineageLoading(false);
});
};
const fetchPipelineDetail = (pipelineFQN: string) => { const fetchPipelineDetail = (pipelineFQN: string) => {
setLoading(true); setLoading(true);
getPipelineByFqn(pipelineFQN, ['owner', 'followers', 'tags', 'tasks']) getPipelineByFqn(pipelineFQN, ['owner', 'followers', 'tags', 'tasks'])
.then((res: AxiosResponse) => { .then((res: AxiosResponse) => {
const { const {
id, id,
deleted,
description, description,
followers, followers,
fullyQualifiedName, fullyQualifiedName,
@ -176,6 +194,7 @@ const PipelineDetailsPage = () => {
setTier(getTierTags(tags)); setTier(getTierTags(tags));
setTags(getTagsWithoutTier(tags)); setTags(getTagsWithoutTier(tags));
setServiceType(serviceType); setServiceType(serviceType);
setDeleted(deleted);
setSlashedPipelineName([ setSlashedPipelineName([
{ {
name: service.name, name: service.name,
@ -201,6 +220,13 @@ const PipelineDetailsPage = () => {
serviceType: serviceType, serviceType: serviceType,
timestamp: 0, timestamp: 0,
}); });
if (!deleted) {
getLineageData();
} else {
setIsLineageLoading(false);
}
setPipelineUrl(pipelineUrl); setPipelineUrl(pipelineUrl);
setTasks(tasks); setTasks(tasks);
}) })
@ -343,14 +369,6 @@ const PipelineDetailsPage = () => {
useEffect(() => { useEffect(() => {
fetchPipelineDetail(pipelineFQN); fetchPipelineDetail(pipelineFQN);
setActiveTab(getCurrentPipelineTab(tab));
getLineageByFQN(pipelineFQN, EntityType.PIPELINE)
.then((res: AxiosResponse) => {
setEntityLineage(res.data);
})
.finally(() => {
setIsLineageLoading(false);
});
}, [pipelineFQN]); }, [pipelineFQN]);
useEffect(() => { useEffect(() => {
@ -365,6 +383,7 @@ const PipelineDetailsPage = () => {
<PipelineDetails <PipelineDetails
activeTab={activeTab} activeTab={activeTab}
addLineageHandler={addLineageHandler} addLineageHandler={addLineageHandler}
deleted={deleted}
description={description} description={description}
descriptionUpdateHandler={descriptionUpdateHandler} descriptionUpdateHandler={descriptionUpdateHandler}
entityLineage={entityLineage} entityLineage={entityLineage}

View File

@ -74,6 +74,7 @@ const TopicDetailsPage: FunctionComponent = () => {
const [replicationFactor, setReplicationFactor] = useState<number>(0); const [replicationFactor, setReplicationFactor] = useState<number>(0);
const [retentionSize, setRetentionSize] = useState<number>(0); const [retentionSize, setRetentionSize] = useState<number>(0);
const [name, setName] = useState<string>(''); const [name, setName] = useState<string>('');
const [deleted, setDeleted] = useState<boolean>(false);
const [schemaText, setSchemaText] = useState<string>('{}'); const [schemaText, setSchemaText] = useState<string>('{}');
const [slashedTopicName, setSlashedTopicName] = useState< const [slashedTopicName, setSlashedTopicName] = useState<
@ -121,6 +122,7 @@ const TopicDetailsPage: FunctionComponent = () => {
.then((res: AxiosResponse) => { .then((res: AxiosResponse) => {
const { const {
id, id,
deleted,
description, description,
followers, followers,
fullyQualifiedName, fullyQualifiedName,
@ -154,6 +156,7 @@ const TopicDetailsPage: FunctionComponent = () => {
setMaximumMessageSize(maximumMessageSize); setMaximumMessageSize(maximumMessageSize);
setReplicationFactor(replicationFactor); setReplicationFactor(replicationFactor);
setRetentionSize(retentionSize); setRetentionSize(retentionSize);
setDeleted(deleted);
setSlashedTopicName([ setSlashedTopicName([
{ {
name: service.name, name: service.name,
@ -261,6 +264,7 @@ const TopicDetailsPage: FunctionComponent = () => {
<TopicDetails <TopicDetails
activeTab={activeTab} activeTab={activeTab}
cleanupPolicies={cleanupPolicies} cleanupPolicies={cleanupPolicies}
deleted={deleted}
description={description} description={description}
descriptionUpdateHandler={descriptionUpdateHandler} descriptionUpdateHandler={descriptionUpdateHandler}
entityName={name} entityName={name}

View File

@ -57,6 +57,7 @@ const ExplorePage: FunctionComponent = () => {
const [pipelineCount, setPipelineCount] = useState<number>(0); const [pipelineCount, setPipelineCount] = useState<number>(0);
const [dbtModelCount, setDbtModelCount] = useState<number>(0); const [dbtModelCount, setDbtModelCount] = useState<number>(0);
const [searchResult, setSearchResult] = useState<ExploreSearchData>(); const [searchResult, setSearchResult] = useState<ExploreSearchData>();
const [showDeleted, setShowDeleted] = useState(false);
const [initialSortField] = useState<string>( const [initialSortField] = useState<string>(
searchQuery searchQuery
? tabsInfo[getCurrentTab(tab) - 1].sortField ? tabsInfo[getCurrentTab(tab) - 1].sortField
@ -107,7 +108,8 @@ const ExplorePage: FunctionComponent = () => {
initialFilter, initialFilter,
emptyValue, emptyValue,
emptyValue, emptyValue,
entity entity,
showDeleted
) )
); );
@ -166,7 +168,8 @@ const ExplorePage: FunctionComponent = () => {
d.filters, d.filters,
d.sortField, d.sortField,
d.sortOrder, d.sortOrder,
d.searchIndex d.searchIndex,
showDeleted
); );
}); });
@ -198,7 +201,7 @@ const ExplorePage: FunctionComponent = () => {
useEffect(() => { useEffect(() => {
fetchCounts(); fetchCounts();
}, [searchText]); }, [searchText, showDeleted]);
useEffect(() => { useEffect(() => {
AppState.explorePageTab = tab; AppState.explorePageTab = tab;
@ -260,6 +263,7 @@ const ExplorePage: FunctionComponent = () => {
searchQuery={searchQuery} searchQuery={searchQuery}
searchResult={searchResult} searchResult={searchResult}
searchText={searchText} searchText={searchText}
showDeleted={showDeleted}
sortValue={initialSortField} sortValue={initialSortField}
tab={tab} tab={tab}
tabCounts={{ tabCounts={{
@ -274,6 +278,7 @@ const ExplorePage: FunctionComponent = () => {
updatePipelineCount={handlePipelineCount} updatePipelineCount={handlePipelineCount}
updateTableCount={handleTableCount} updateTableCount={handleTableCount}
updateTopicCount={handleTopicCount} updateTopicCount={handleTopicCount}
onShowDeleted={(checked) => setShowDeleted(checked)}
/> />
</PageContainerV1> </PageContainerV1>
)} )}

View File

@ -146,6 +146,7 @@ const TourPage = () => {
searchQuery="" searchQuery=""
searchResult={exploreSearchResult as unknown as ExploreSearchData} searchResult={exploreSearchResult as unknown as ExploreSearchData}
searchText="" searchText=""
showDeleted={false}
sortValue="" sortValue=""
tab="" tab=""
tabCounts={explorePageCounts} tabCounts={explorePageCounts}
@ -154,6 +155,9 @@ const TourPage = () => {
updatePipelineCount={handleCountChange} updatePipelineCount={handleCountChange}
updateTableCount={handleCountChange} updateTableCount={handleCountChange}
updateTopicCount={handleCountChange} updateTopicCount={handleCountChange}
onShowDeleted={() => {
return;
}}
/> />
); );

View File

@ -18,7 +18,8 @@ export const getSearchAPIQuery = (
filters: string, filters: string,
sortField: string, sortField: string,
sortOrder: string, sortOrder: string,
searchIndex: string searchIndex: string,
onlyDeleted = false
): string => { ): string => {
const start = (from - 1) * size; const start = (from - 1) * size;
const query = queryString const query = queryString
@ -29,7 +30,9 @@ export const getSearchAPIQuery = (
return `q=${query}${ return `q=${query}${
filters ? ` AND ${filters}` : '' filters ? ` AND ${filters}` : ''
}&from=${start}&size=${size}${sortField ? `&sort_field=${sortField}` : ''}${ }&from=${start}&size=${size}${onlyDeleted ? '&deleted=true' : ''}${
sortOrder ? `&sort_order=${sortOrder}` : '' sortField ? `&sort_field=${sortField}` : ''
}${searchIndex ? `&index=${searchIndex}` : ''}`; }${sortOrder ? `&sort_order=${sortOrder}` : ''}${
searchIndex ? `&index=${searchIndex}` : ''
}`;
}; };