);
diff --git a/openmetadata-ui/src/main/resources/ui/src/components/app-bar/Appbar.tsx b/openmetadata-ui/src/main/resources/ui/src/components/app-bar/Appbar.tsx
index 8b580e80cd8..83a90f3a9b7 100644
--- a/openmetadata-ui/src/main/resources/ui/src/components/app-bar/Appbar.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/components/app-bar/Appbar.tsx
@@ -30,7 +30,10 @@ import {
} from '../../constants/constants';
import { urlGitbookDocs, urlJoinSlack } from '../../constants/url.const';
import { useAuth } from '../../hooks/authHooks';
-import { addToRecentSearched } from '../../utils/CommonUtils';
+import {
+ addToRecentSearched,
+ getNonDeletedTeams,
+} from '../../utils/CommonUtils';
import SVGIcons, { Icons } from '../../utils/SvgUtils';
import { COOKIE_VERSION } from '../Modals/WhatsNewModal/whatsNewData';
import NavBar from '../nav-bar/NavBar';
@@ -146,7 +149,7 @@ const Appbar: React.FC = (): JSX.Element => {
const roles = currentUser?.roles;
- const teams = currentUser?.teams;
+ const teams = getNonDeletedTeams(currentUser?.teams ?? []);
return (
@@ -166,10 +169,10 @@ const Appbar: React.FC = (): JSX.Element => {
) : null}
- {(teams?.length ?? 0) > 0 ? (
+ {teams.length > 0 ? (
Teams
- {teams?.map((t, i) => (
+ {teams.map((t, i) => (
{t.displayName}
diff --git a/openmetadata-ui/src/main/resources/ui/src/generated/entity/teams/user.ts b/openmetadata-ui/src/main/resources/ui/src/generated/entity/teams/user.ts
index fb98022334e..cc126b37cef 100644
--- a/openmetadata-ui/src/main/resources/ui/src/generated/entity/teams/user.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/generated/entity/teams/user.ts
@@ -144,6 +144,10 @@ export interface FieldChange {
* the relationship of a table `belongs to a` database.
*/
export interface EntityReference {
+ /**
+ * If true the entity referred to has been soft-deleted.
+ */
+ deleted?: boolean;
/**
* Optional description of entity.
*/
diff --git a/openmetadata-ui/src/main/resources/ui/src/jsons/en.ts b/openmetadata-ui/src/main/resources/ui/src/jsons/en.ts
index b7207d92214..628e5e637bf 100644
--- a/openmetadata-ui/src/main/resources/ui/src/jsons/en.ts
+++ b/openmetadata-ui/src/main/resources/ui/src/jsons/en.ts
@@ -60,6 +60,7 @@ const jsonData = {
'fetch-thread-error': 'Error while fetching threads!',
'fetch-updated-conversation-error':
'Error while fetching updated conversation!',
+ 'fetch-user-details-error': 'Error while fetching user details!',
'fetch-service-error': 'Error while fetching service details!',
'fetch-teams-error': 'Error while fetching teams!',
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/UserPage/UserPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/UserPage/UserPage.component.tsx
index 99fc2efaa5e..c0aa1259c3c 100644
--- a/openmetadata-ui/src/main/resources/ui/src/pages/UserPage/UserPage.component.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/UserPage/UserPage.component.tsx
@@ -1,4 +1,17 @@
-import { AxiosResponse } from 'axios';
+/*
+ * Copyright 2021 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 { AxiosError, AxiosResponse } from 'axios';
import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { getUserByName } from '../../axiosAPIs/userAPI';
@@ -6,30 +19,55 @@ import PageContainerV1 from '../../components/containers/PageContainerV1';
import Loader from '../../components/Loader/Loader';
import Users from '../../components/Users/Users.component';
import { User } from '../../generated/entity/teams/user';
+import useToastContext from '../../hooks/useToastContext';
+import jsonData from '../../jsons/en';
+import { getErrorText } from '../../utils/StringsUtils';
const UserPage = () => {
+ const showToast = useToastContext();
const { username } = useParams<{ [key: string]: string }>();
const [isLoading, setIsLoading] = useState(true);
const [userData, setUserData] = useState({} as User);
const [isError, setIsError] = useState(false);
+ const handleShowErrorToast = (errMessage: string) => {
+ showToast({
+ variant: 'error',
+ body: errMessage,
+ });
+ };
+
const fetchUserData = () => {
getUserByName(username, 'profile,roles,teams,follows,owns')
.then((res: AxiosResponse) => {
- setUserData(res.data);
+ if (res.data) {
+ setUserData(res.data);
+ } else {
+ throw jsonData['api-error-messages']['unexpected-server-response'];
+ }
})
- .catch(() => {
+ .catch((err: AxiosError) => {
+ const errMsg = getErrorText(
+ err,
+ jsonData['api-error-messages']['fetch-user-details-error']
+ );
+ handleShowErrorToast(errMsg);
setIsError(true);
})
.finally(() => setIsLoading(false));
};
- const errorPlaceholder = () => {
+ const ErrorPlaceholder = () => {
return (
-
-
+
+
No user available with{' '}
- {username} username.
+
+ {username}
+ {' '}
+ username.
);
@@ -46,7 +84,7 @@ const UserPage = () => {
) : !isError ? (
) : (
- errorPlaceholder()
+
)}
);
diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/UserPage/UserPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/UserPage/UserPage.test.tsx
new file mode 100644
index 00000000000..4542e71ffe1
--- /dev/null
+++ b/openmetadata-ui/src/main/resources/ui/src/pages/UserPage/UserPage.test.tsx
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2021 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 { findByTestId, findByText, render } from '@testing-library/react';
+import React from 'react';
+import { MemoryRouter } from 'react-router-dom';
+import { getUserByName } from '../../axiosAPIs/userAPI';
+import UserPage from './UserPage.component';
+
+const mockUserData = {
+ id: 'd6764107-e8b4-4748-b256-c86fecc66064',
+ name: 'xyz',
+ displayName: 'XYZ',
+ version: 0.1,
+ updatedAt: 1648704499857,
+ updatedBy: 'xyz',
+ email: 'xyz@gmail.com',
+ href: 'http://localhost:8585/api/v1/users/d6764107-e8b4-4748-b256-c86fecc66064',
+ isAdmin: false,
+ profile: {
+ images: {
+ image:
+ 'https://lh3.googleusercontent.com/a-/AOh14Gh8NPux8jEPIuyPWOxAB1od9fGN188Kcp5HeXgc=s96-c',
+ image24:
+ 'https://lh3.googleusercontent.com/a-/AOh14Gh8NPux8jEPIuyPWOxAB1od9fGN188Kcp5HeXgc=s24-c',
+ image32:
+ 'https://lh3.googleusercontent.com/a-/AOh14Gh8NPux8jEPIuyPWOxAB1od9fGN188Kcp5HeXgc=s32-c',
+ image48:
+ 'https://lh3.googleusercontent.com/a-/AOh14Gh8NPux8jEPIuyPWOxAB1od9fGN188Kcp5HeXgc=s48-c',
+ image72:
+ 'https://lh3.googleusercontent.com/a-/AOh14Gh8NPux8jEPIuyPWOxAB1od9fGN188Kcp5HeXgc=s72-c',
+ image192:
+ 'https://lh3.googleusercontent.com/a-/AOh14Gh8NPux8jEPIuyPWOxAB1od9fGN188Kcp5HeXgc=s192-c',
+ image512:
+ 'https://lh3.googleusercontent.com/a-/AOh14Gh8NPux8jEPIuyPWOxAB1od9fGN188Kcp5HeXgc=s512-c',
+ },
+ },
+ teams: [
+ {
+ id: '3362fe18-05ad-4457-9632-84f22887dda6',
+ type: 'team',
+ name: 'Finance',
+ description: 'This is Finance description.',
+ displayName: 'Finance',
+ deleted: false,
+ href: 'http://localhost:8585/api/v1/teams/3362fe18-05ad-4457-9632-84f22887dda6',
+ },
+ {
+ id: '5069ddd4-d47e-4b2c-a4c4-4c849b97b7f9',
+ type: 'team',
+ name: 'Data_Platform',
+ description: 'This is Data_Platform description.',
+ displayName: 'Data_Platform',
+ deleted: false,
+ href: 'http://localhost:8585/api/v1/teams/5069ddd4-d47e-4b2c-a4c4-4c849b97b7f9',
+ },
+ {
+ id: '7182cc43-aebc-419d-9452-ddbe2fc4e640',
+ type: 'team',
+ name: 'Customer_Support',
+ description: 'This is Customer_Support description.',
+ displayName: 'Customer_Support',
+ deleted: false,
+ href: 'http://localhost:8585/api/v1/teams/7182cc43-aebc-419d-9452-ddbe2fc4e640',
+ },
+ ],
+ owns: [],
+ follows: [],
+ deleted: false,
+ roles: [
+ {
+ id: 'ce4df2a5-aaf5-4580-8556-254f42574aa7',
+ type: 'role',
+ name: 'DataConsumer',
+ description:
+ 'Users with Data Consumer role use different data assets for their day to day work.',
+ displayName: 'Data Consumer',
+ deleted: false,
+ href: 'http://localhost:8585/api/v1/roles/ce4df2a5-aaf5-4580-8556-254f42574aa7',
+ },
+ ],
+};
+
+jest.mock('react-router-dom', () => ({
+ useParams: jest.fn().mockImplementation(() => ({ username: 'xyz' })),
+}));
+
+jest.mock('../../components/Loader/Loader', () => {
+ return jest.fn().mockReturnValue(
Loader
);
+});
+
+jest.mock('../../components/Users/Users.component', () => {
+ return jest.fn().mockReturnValue(
User Component
);
+});
+
+jest.mock('../../axiosAPIs/userAPI', () => ({
+ getUserByName: jest
+ .fn()
+ .mockImplementation(() => Promise.resolve({ data: mockUserData })),
+}));
+
+describe('Test the User Page', () => {
+ it('Should render the user component', async () => {
+ const { container } = render(
, { wrapper: MemoryRouter });
+
+ const userComponent = await findByText(container, /User Component/i);
+
+ expect(userComponent).toBeInTheDocument();
+ });
+
+ it('Should render error placeholder if api fails', async () => {
+ (getUserByName as jest.Mock).mockImplementationOnce(() =>
+ Promise.reject({
+ response: {
+ data: {
+ message: 'Error',
+ },
+ },
+ })
+ );
+ const { container } = render(
, { wrapper: MemoryRouter });
+
+ const errorPlaceholder = await findByTestId(container, 'error');
+
+ expect(errorPlaceholder).toBeInTheDocument();
+ });
+});
diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx
index 4adc11cb305..bf8dbb94ff0 100644
--- a/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx
+++ b/openmetadata-ui/src/main/resources/ui/src/utils/CommonUtils.tsx
@@ -35,6 +35,7 @@ import { UrlEntityCharRegEx } from '../constants/regex.constants';
import { TabSpecificField } from '../enums/entity.enum';
import { Ownership } from '../enums/mydata.enum';
import {
+ EntityReference,
EntityReference as UserTeams,
User,
} from '../generated/entity/teams/user';
@@ -499,3 +500,12 @@ export const getRandomColor = (name: string) => {
export const isUrlFriendlyName = (value: string) => {
return !UrlEntityCharRegEx.test(value);
};
+
+/**
+ * Take teams data and filter out the non deleted teams
+ * @param teams - teams array
+ * @returns - non deleted team
+ */
+export const getNonDeletedTeams = (teams: EntityReference[]) => {
+ return teams.filter((t) => !t.deleted);
+};