fix: add and remove users in a team (#18776)

* Improvement : Teams api to update and remove users

* fix: add and remove user apis, with e2e test case

* fix: test for Teams page

* fix: e2e test for delete user

* fix: remove user field from fetchTeamAdvancedDetails function

* fix: test for loadAdvancedDetails

* fix: response api for add and remove user

* fix: userCount after delete

* fix: update users call

---------

Co-authored-by: sonikashah <sonikashah94@gmail.com>
Co-authored-by: sonika-shah <58761340+sonika-shah@users.noreply.github.com>
This commit is contained in:
Pranita Fulsundar 2024-11-27 19:13:16 +05:30 committed by GitHub
parent 9985286cdb
commit fdd8c5878c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 146 additions and 49 deletions

View File

@ -158,7 +158,7 @@ test.describe('Teams Page', () => {
)
).toHaveClass(/active/);
const updateTeamResponse = page.waitForResponse('/api/v1/teams/*');
const updateTeamResponse = page.waitForResponse('/api/v1/users*');
// Update the team with the new user
await page.locator('[data-testid="selectable-list-update-btn"]').click();
@ -188,7 +188,7 @@ test.describe('Teams Page', () => {
)
.click();
const updateTeamResponse = page.waitForResponse('/api/v1/teams/*');
const updateTeamResponse = page.waitForResponse('/api/v1/users*');
await page.locator('[data-testid="selectable-list-update-btn"]').click();
await updateTeamResponse;
@ -562,4 +562,77 @@ test.describe('Teams Page', () => {
await afterAction();
}
});
test('Delete a user from the table', async ({ page }) => {
const { apiContext, afterAction } = await getApiContext(page);
const id = uuid();
const table1 = new TableClass();
const team1 = new TeamClass({
name: `pw%percent-${id}`,
displayName: `pw team percent ${id}`,
description: 'playwright team with percent description',
teamType: 'Group',
});
await table1.create(apiContext);
await team1.create(apiContext);
await addTeamOwnerToEntity(page, table1, team1);
await verifyAssetsInTeamsPage(page, table1, team1, 1);
// Navigate to users tab and add new user
await page.locator('[data-testid="users"]').click();
const fetchUsersResponse = page.waitForResponse(
'/api/v1/users?limit=25&isBot=false'
);
await page.locator('[data-testid="add-new-user"]').click();
await fetchUsersResponse;
// Search and select the user
await page
.locator('[data-testid="selectable-list"] [data-testid="searchbar"]')
.fill(user.getUserName());
await page
.locator(
`[data-testid="selectable-list"] [title="${user.getUserName()}"]`
)
.click();
await expect(
page.locator(
`[data-testid="selectable-list"] [title="${user.getUserName()}"]`
)
).toHaveClass(/active/);
const updateTeamResponse = page.waitForResponse('/api/v1/users*');
// Update the team with the new user
await page.locator('[data-testid="selectable-list-update-btn"]').click();
await updateTeamResponse;
// Verify the user is added to the team
await expect(
page.locator(`[data-row-key="${userName.toLowerCase()}"]`)
).toBeVisible();
await page
.locator(`[data-row-key="${userName.toLowerCase()}"]`)
.getByTestId('remove-user-btn')
.click();
const updatedTeamResponse = page.waitForResponse('api/v1/users*');
await page.getByRole('button', { name: 'confirm' }).click();
await updatedTeamResponse;
await expect(
page.locator(`[data-row-key="${userName.toLowerCase()}"]`)
).not.toBeVisible();
await afterAction();
});
});

View File

@ -30,7 +30,7 @@ export const getTabs = (
},
users: {
name: t('label.user-plural'),
count: currentTeam.users?.length ?? 0,
count: currentTeam.userCount ?? 0,
key: TeamsPageTab.USERS,
},
assets: {

View File

@ -48,7 +48,10 @@ import { searchData } from '../../../../../rest/miscAPI';
import { exportUserOfTeam } from '../../../../../rest/teamsAPI';
import { getUsers } from '../../../../../rest/userAPI';
import { formatUsersResponse } from '../../../../../utils/APIUtils';
import { getEntityName } from '../../../../../utils/EntityUtils';
import {
getEntityName,
getEntityReferenceFromEntity,
} from '../../../../../utils/EntityUtils';
import { getSettingsPathWithFqn } from '../../../../../utils/RouterUtils';
import { commonUserDetailColumns } from '../../../../../utils/Users.util';
import ManageButton from '../../../../common/EntityPageInfos/ManageButton/ManageButton';
@ -75,7 +78,7 @@ export const UserTab = ({
const [deletingUser, setDeletingUser] = useState<EntityReference>();
const { showModal } = useEntityExportModalProvider();
const handleRemoveClick = (id: string) => {
const user = currentTeam.users?.find((u) => u.id === id);
const user = usersList?.find((u) => u.id === id);
setDeletingUser(user);
};
const [isLoading, setIsLoading] = useState(true);
@ -91,6 +94,13 @@ export const UserTab = ({
showPagination,
} = usePaging(PAGE_SIZE_MEDIUM);
const usersList = useMemo(() => {
return users.map((item) =>
getEntityReferenceFromEntity(item, EntityType.USER)
);
}, [users]);
const isGroupType = useMemo(
() => currentTeam.teamType === TeamType.Group,
[currentTeam.teamType]
@ -313,7 +323,7 @@ export const UserTab = ({
<Space>
<UserSelectableList
hasPermission
selectedUsers={currentTeam.users ?? []}
selectedUsers={currentTeam?.users ?? []}
onUpdate={onAddUser}>
<Tooltip placement="topRight" title={addUserButtonTitle}>
<Button
@ -375,7 +385,7 @@ export const UserTab = ({
{users.length > 0 && permission.EditAll && (
<UserSelectableList
hasPermission
selectedUsers={currentTeam.users ?? []}
selectedUsers={currentTeam?.users ?? []}
onUpdate={onAddUser}>
<Button data-testid="add-new-user" type="primary">
{t('label.add-entity', { entity: t('label.user') })}

View File

@ -155,7 +155,7 @@ describe('Test Teams Page', () => {
expect(mockGetTeamByName.mock.calls[0]).toEqual([
'test',
{
fields: ['users', 'userCount', 'parents', 'profile', 'owners'],
fields: ['userCount', 'parents', 'profile', 'owners'],
include: 'all',
},
]);

View File

@ -40,9 +40,11 @@ import { useFqn } from '../../hooks/useFqn';
import { searchData } from '../../rest/miscAPI';
import {
createTeam,
deleteUserFromTeam,
getTeamByName,
getTeams,
patchTeamDetail,
updateUsersFromTeam,
} from '../../rest/teamsAPI';
import { updateUserDetail } from '../../rest/userAPI';
import { getEntityReferenceFromEntity } from '../../utils/EntityUtils';
@ -75,7 +77,7 @@ const TeamsPage = () => {
const [entityPermissions, setEntityPermissions] =
useState<OperationPermission>(DEFAULT_ENTITY_PERMISSION);
const [isFetchingAdvancedDetails, setFetchingAdvancedDetails] =
useState<boolean>(false);
useState<boolean>(true);
const [isFetchAllTeamAdvancedDetails, setFetchAllTeamAdvancedDetails] =
useState<boolean>(false);
@ -217,7 +219,6 @@ const TeamsPage = () => {
try {
const data = await getTeamByName(name, {
fields: [
TabSpecificField.USERS,
TabSpecificField.USER_COUNT,
TabSpecificField.PARENTS,
TabSpecificField.PROFILE,
@ -369,15 +370,18 @@ const TeamsPage = () => {
* @param data
*/
const addUsersToTeam = async (data: Array<EntityReference>) => {
if (!isUndefined(selectedTeam) && !isUndefined(selectedTeam.users)) {
const updatedTeam = {
...selectedTeam,
users: data,
};
const jsonPatch = compare(selectedTeam, updatedTeam);
if (!isUndefined(selectedTeam)) {
try {
const res = await patchTeamDetail(selectedTeam.id, jsonPatch);
setSelectedTeam((prev) => ({ ...prev, ...res }));
const res = await updateUsersFromTeam(selectedTeam.id, data);
if (res) {
setSelectedTeam((prev) => ({
...prev,
users: data,
userCount: data.length,
}));
} else {
throw new Error(t('server.unexpected-response'));
}
} catch (error) {
showErrorToast(
error as AxiosError,
@ -393,38 +397,27 @@ const TeamsPage = () => {
* Take user id and remove that user from the team
* @param id - user id
*/
const removeUserFromTeam = (id: string) => {
const newUsers = selectedTeam?.users?.filter((user) => {
return user.id !== id;
});
const updatedTeam = {
...selectedTeam,
users: newUsers,
};
const jsonPatch = compare(selectedTeam, updatedTeam);
return new Promise<void>((resolve) => {
patchTeamDetail(selectedTeam.id, jsonPatch)
.then((res) => {
if (res) {
setSelectedTeam((prev) => ({ ...prev, ...res }));
} else {
throw t('server.unexpected-response');
}
const removeUserFromTeam = async (id: string) => {
const updatedUsers = selectedTeam?.users?.filter((user) => user.id !== id);
try {
const res = await deleteUserFromTeam(selectedTeam.id, id);
if (res) {
setSelectedTeam((prev) => ({
...prev,
users: updatedUsers,
userCount: updatedUsers?.length,
}));
} else {
throw new Error(t('server.unexpected-response'));
}
} catch (error) {
showErrorToast(
error as AxiosError,
t('server.entity-updating-error', {
entity: t('label.team'),
})
.catch((error: AxiosError) => {
showErrorToast(
error,
t('server.entity-updating-error', {
entity: t('label.team'),
})
);
})
.finally(() => {
resolve();
});
});
);
}
};
const onDescriptionUpdate = async (updatedHTML: string) => {

View File

@ -17,6 +17,7 @@ import { PagingResponse, RestoreRequestType } from 'Models';
import { CSVImportAsyncResponse } from '../components/BulkImport/BulkEntityImport.interface';
import { CSVExportResponse } from '../components/Entity/EntityExportModalProvider/EntityExportModalProvider.interface';
import { CreateTeam } from '../generated/api/teams/createTeam';
import { EntityReference } from '../generated/entity/data/table';
import { Team } from '../generated/entity/teams/team';
import { TeamHierarchy } from '../generated/entity/teams/teamHierarchy';
import { ListParams } from '../interface/API.interface';
@ -75,6 +76,26 @@ export const patchTeamDetail = async (id: string, data: Operation[]) => {
return response.data;
};
export const deleteUserFromTeam = async (teamId: string, userId: string) => {
const response = await APIClient.delete<Operation[], AxiosResponse<Team>>(
`/teams/${teamId}/users/${userId}`
);
return response.data;
};
export const updateUsersFromTeam = async (
id: string,
data: EntityReference[]
) => {
const response = await APIClient.put<Operation[], AxiosResponse<Team>>(
`/teams/${id}/users`,
data
);
return response.data;
};
export const deleteTeam = async (id: string) => {
const response = await APIClient.delete<Team>(`/teams/${id}`);