mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-12-24 14:08:45 +00:00
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:
parent
9985286cdb
commit
fdd8c5878c
@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -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') })}
|
||||
|
||||
@ -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',
|
||||
},
|
||||
]);
|
||||
|
||||
@ -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) => {
|
||||
|
||||
@ -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}`);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user