Skip to content
11 changes: 7 additions & 4 deletions dashboard/src/components/EntityDisplayImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import { useEffect, useState } from "react";
import { Avatar, Skeleton } from "@mui/material";
import { getEntityIconPath } from "../utils/Utils";
import axios from "axios";

const DisplayImage = ({
entity,
Expand All @@ -36,10 +37,12 @@ const DisplayImage = ({
let entityData = { ...entity, ...{ isProcess: isProcess } };
let imagePath: any = getEntityIconPath({ entityData: entityData });
try {
const response = await fetch(imagePath);
const contentType: any = response.headers.get("Content-Type");
const response = await axios.get(imagePath, {
responseType: "blob"
});
const contentType: any = response.headers["content-type"];

if (contentType.startsWith("image/")) {
if (contentType && contentType.startsWith("image/")) {
let cache = { [entityData.guid]: imagePath };
setCheckEntityImage(cache);
setImageUrl(getEntityIconPath({ entityData: entityData }));
Expand All @@ -48,7 +51,7 @@ const DisplayImage = ({
getEntityIconPath({ entityData: entityData, errorUrl: imagePath })
);
}
} catch (error) {
} catch (_error) {
setImageUrl(
getEntityIconPath({ entityData: entityData, errorUrl: imagePath })
);
Expand Down
5 changes: 3 additions & 2 deletions dashboard/src/components/ShowMore/DrawerBodyChipView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import SearchIcon from "@mui/icons-material/Search";
import ErrorRoundedIcon from "@mui/icons-material/ErrorRounded";
import { Link as MuiLink } from "@mui/material";
import { cloneDeep } from "@utils/Helper";
import { EntityStatus } from "@utils/EntityStatus";
Comment thread
Brijesh619 marked this conversation as resolved.
Outdated
Comment thread
Brijesh619 marked this conversation as resolved.
Outdated

const CHIP_MAX_WIDTH = "200px";

Expand Down Expand Up @@ -328,11 +329,11 @@ const DrawerBodyChipView = ({
</EllipsisText>
}
onDelete={
!isEmpty(removeApiMethod) && !isDeleteIcon
currentEntity?.status !== EntityStatus.DELETED && !isEmpty(removeApiMethod) && !isDeleteIcon
? () => {
handleDelete(obj[displayKey] || obj);
}
: isDeleteIcon && obj.count > 1
: currentEntity?.status !== EntityStatus.DELETED && isDeleteIcon && obj.count > 1
? () => {
const searchParams = new URLSearchParams();
searchParams.set("tabActive", "classification");
Expand Down
15 changes: 11 additions & 4 deletions dashboard/src/components/ShowMore/ShowMoreView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import Typography from "@mui/material/Typography";
import Chip from "@mui/material/Chip";
import MuiLink from "@mui/material/Link";
import { LightTooltip } from "../muiComponents";
import { useRef, useState } from "react";
import { useEffect, useRef, useState } from "react";
import { EllipsisText } from "../commonComponents";
import { extractKeyValueFromEntity, isEmpty, serverError } from "@utils/Utils";
import { useAppDispatch, useAppSelector } from "@hooks/reducerHook";
Expand All @@ -31,8 +31,9 @@ import ErrorRoundedIcon from "@mui/icons-material/ErrorRounded";
import { fetchGlossaryData } from "@redux/slice/glossarySlice";
import { fetchGlossaryDetails } from "@redux/slice/glossaryDetailsSlice";
import ShowMoreDrawer from "./ShowMoreDrawer";
import { openDrawer } from "@redux/slice/drawerSlice";
import { openDrawer, closeDrawer } from "@redux/slice/drawerSlice";
import { cloneDeep } from "@utils/Helper";
import { EntityStatus } from "@utils/EntityStatus";
Comment thread
Brijesh619 marked this conversation as resolved.
Outdated

const CHIP_MAX_WIDTH = "200px";

Expand Down Expand Up @@ -73,6 +74,12 @@ const ShowMoreView = ({
const gType = searchParams.get("gtype");
const dispatchApi = useAppDispatch();

useEffect(() => {
Comment thread
Brijesh619 marked this conversation as resolved.
return () => {
dispatchApi(closeDrawer());
};
}, [dispatchApi]);

const { classificationData = {} }: any = useAppSelector(
(state: any) => state.classification
);
Expand Down Expand Up @@ -310,13 +317,13 @@ const ShowMoreView = ({
}
component="a"
onDelete={
!isEmpty(removeApiMethod) && !isDeleteIcon
!isEmpty(removeApiMethod) && !isDeleteIcon && currentEntity?.status !== EntityStatus.DELETED
? () => {
// Handle undefined displayKey by extracting a string value
const deleteValue = obj[displayKey] || obj.displayText || obj.text || obj.name || '';
handleDelete(deleteValue);
}
: isDeleteIcon && obj.count > 1
: isDeleteIcon && obj.count > 1 && currentEntity?.status !== EntityStatus.DELETED
? () => {
const searchParams = new URLSearchParams();
searchParams.set("tabActive", "classification");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -980,6 +980,25 @@ describe('DrawerBodyChipView', () => {
expect(mockNavigate).toHaveBeenCalled();
}
});

it('should not show delete icon for DELETED entities', () => {
render(
<TestWrapper>
<DrawerBodyChipView
{...defaultProps}
currentEntity={{ ...mockCurrentEntity, status: 'DELETED' }}
/>
</TestWrapper>
);

const chips = screen.getAllByTestId('chip');
chips.forEach(chip => {
const deleteIcon = chip.querySelector('[data-testid="chip-delete-icon"]');
expect(deleteIcon).toBeNull();
const deleteButton = chip.querySelector('[data-testid="chip-delete-button"]');
expect(deleteButton).toBeNull();
});
});
});

describe('Modal Functionality', () => {
Expand Down
23 changes: 23 additions & 0 deletions dashboard/src/components/ShowMore/__tests__/ShowMoreView.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ jest.mock('@redux/slice/drawerSlice', () => ({
openDrawer: jest.fn((id: string) => ({
type: 'drawer/openDrawer',
payload: id
})),
closeDrawer: jest.fn(() => ({
type: 'drawer/closeDrawer'
}))
}));

Expand Down Expand Up @@ -739,6 +742,26 @@ describe('ShowMoreView', () => {
});

describe('Delete Icon Functionality', () => {
it('should not show delete button when currentEntity status is DELETED', () => {
const dataWithCount = [
{ typeName: 'Tag1' }
];

render(
<TestWrapper>
<ShowMoreView
{...defaultProps}
data={dataWithCount}
removeApiMethod={jest.fn()}
currentEntity={{ guid: 'entity-guid-123', status: 'DELETED' }}
/>
</TestWrapper>
);

expect(screen.queryByTestId('chip-ondelete-button')).not.toBeInTheDocument();
});


it('should show count when isDeleteIcon is true and count > 1', () => {
const dataWithCount = [
{ typeName: 'Tag1', count: 2 },
Expand Down
22 changes: 10 additions & 12 deletions dashboard/src/components/__tests__/EntityDisplayImage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import React from 'react'
Comment thread
Brijesh619 marked this conversation as resolved.
import { render, waitFor, act } from '@testing-library/react'
import DisplayImage from '../EntityDisplayImage'
Comment thread
Brijesh619 marked this conversation as resolved.
import axios from 'axios'

// Import Utils to spy on it
import * as Utils from '../../utils/Utils'
Expand All @@ -41,18 +42,15 @@ jest.mock('../../utils/Utils', () => ({
}))

const mockFetch = (contentType: string | null, shouldReject?: boolean) => {
if (shouldReject) {
(global as any).fetch = jest.fn().mockRejectedValue(new Error('fetch failed'))
return
}
(global as any).fetch = jest.fn().mockResolvedValue({
ok: true,
headers: {
get: jest.fn((header: string) => {
return header === 'Content-Type' ? (contentType || '') : null
})
}
})
if (shouldReject) {
jest.spyOn(axios, 'get').mockRejectedValue(new Error('fetch failed'))
return
}
jest.spyOn(axios, 'get').mockResolvedValue({
headers: {
"content-type": contentType || ''
}
})
}

describe('EntityDisplayImage', () => {
Expand Down
4 changes: 4 additions & 0 deletions dashboard/src/styles/propertiesTab.scss
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,7 @@
.audit-attributes-item:nth-child(3) {
flex: 0 0 100%;
}

.text-underline {
Comment thread
Brijesh619 marked this conversation as resolved.
text-decoration: underline;
}
27 changes: 27 additions & 0 deletions dashboard/src/utils/EntityStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/

export enum EntityStatus {
Comment thread
Brijesh619 marked this conversation as resolved.
ACTIVE = "ACTIVE",
DELETED = "DELETED",
PURGED = "PURGED"
}

export const isEntityModificationAllowed = (status: EntityStatus | string | undefined): boolean => {
Comment thread
Brijesh619 marked this conversation as resolved.
if (!status) return true;
return status !== EntityStatus.DELETED && status !== EntityStatus.PURGED;
};
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ const ClassificationCoverage = memo(
aria-label="Open classification search"
>
{numberFormatWithComma(typesInUse)} of{" "}
{numberFormatWithComma(classificationTypeDefinitions)}
{numberFormatWithComma(classificationTypeDefinitions)}{" "}
classification types are in use (have at least one entity).
</Typography>
</Stack>
Expand Down
Loading
Loading