Skip to content

Commit

Permalink
Fix #2884 UI From Roles page, Admin should be able to assign the role…
Browse files Browse the repository at this point in the history
… to a team (#3147)
  • Loading branch information
Sachin-chaurasiya committed Mar 4, 2022
1 parent 13e24c5 commit 875404f
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import { AxiosResponse } from 'axios';
import { Operation } from 'fast-json-patch';
import { UserProfile } from 'Models';
import { SearchIndex } from '../enums/search.enum';
import { User } from '../generated/entity/teams/user';
import { getURLWithQueryFields } from '../utils/APIUtils';
import APIClient from './index';
Expand Down Expand Up @@ -101,6 +102,6 @@ export const updateUser = (

export const getUserCounts = () => {
return APIClient.get(
'/search/query?q=*&from=0&size=0&index=user_search_index'
`/search/query?q=*&from=0&size=0&index=${SearchIndex.USER}`
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@ export enum SearchIndex {
TOPIC = 'topic_search_index',
DASHBOARD = 'dashboard_search_index',
PIPELINE = 'pipeline_search_index',
USER = 'user_search_index',
TEAM = 'team_search_index',
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
updatePolicy,
updateRole,
} from '../../axiosAPIs/rolesAPI';
import { patchTeamDetail } from '../../axiosAPIs/teamsAPI';
import { getUserCounts } from '../../axiosAPIs/userAPI';
import { Button } from '../../components/buttons/Button/Button';
import Description from '../../components/common/description/Description';
Expand All @@ -47,11 +48,13 @@ import {
Rule,
} from '../../generated/entity/policies/accessControl/rule';
import { Role } from '../../generated/entity/teams/role';
import { Team } from '../../generated/entity/teams/team';
import { EntityReference } from '../../generated/entity/teams/user';
import { useAuth } from '../../hooks/authHooks';
import useToastContext from '../../hooks/useToastContext';
import { getActiveCatClass, isEven } from '../../utils/CommonUtils';
import SVGIcons from '../../utils/SvgUtils';
import AddUsersModal from '../teams/AddUsersModal';
import Form from '../teams/Form';
import UserCard from '../teams/UserCard';
import { Policy } from './policy.interface';
Expand Down Expand Up @@ -90,6 +93,9 @@ const RolesPage = () => {

const [defaultRole, setDefaultRole] = useState<Role>();

const [teamList, setTeamList] = useState<Array<Team>>([]);
const [isAddingTeams, setIsAddingTeams] = useState<boolean>(false);

const onNewDataChange = (data: Role, forceSet = false) => {
if (errorData || forceSet) {
const errData: { [key: string]: string } = {};
Expand Down Expand Up @@ -159,7 +165,7 @@ const RolesPage = () => {

const fetchRoles = () => {
setIsLoading(true);
getRoles(['policy', 'users'])
getRoles(['policy', 'users', 'teams'])
.then((res: AxiosResponse) => {
const { data } = res.data;
setRoles(data);
Expand Down Expand Up @@ -206,7 +212,7 @@ const RolesPage = () => {
const fetchCurrentRole = (name: string, update = false) => {
if (currentRole?.name !== name || update) {
setIsLoading(true);
getRoleByName(name, ['users', 'policy'])
getRoleByName(name, ['users', 'policy', 'teams'])
.then((res: AxiosResponse) => {
setCurrentRole(res.data);
setRoles((pre) => {
Expand Down Expand Up @@ -259,6 +265,39 @@ const RolesPage = () => {
setIsSettingDefaultRole(false);
};

const addTeams = (data: Team[]) => {
const currentRoleReference: EntityReference = {
id: currentRole?.id as string,
type: 'role',
};
const teamsPatchCall = data.map((team) => {
const updatedTeam = {
...team,
defaultRoles: [
...(team.defaultRoles as EntityReference[]),
currentRoleReference,
],
};
const patch = compare(team, updatedTeam);

return patchTeamDetail(team.id, patch);
});
Promise.all(teamsPatchCall)
.then(() => {
setIsAddingTeams(false);
fetchCurrentRole(currentRole?.name as string, true);
})
.catch(() => {
showToast({
variant: 'error',
body: 'Error while adding teams to the role',
});
})
.finally(() => {
setIsAddingTeams(false);
});
};

const onSetDefaultRole = () => {
if (isSettingDefaultRole) {
const updatedRole = { ...currentRole, default: true };
Expand Down Expand Up @@ -401,10 +440,21 @@ const RolesPage = () => {
2,
currentTab
)}`}
data-testid="users"
data-testid="teams"
onClick={() => {
setCurrentTab(2);
}}>
Teams
</button>
<button
className={`tw-pb-2 tw-px-4 tw-gh-tabs ${getActiveTabClass(
3,
currentTab
)}`}
data-testid="users"
onClick={() => {
setCurrentTab(3);
}}>
Users
</button>
</nav>
Expand Down Expand Up @@ -576,6 +626,59 @@ const RolesPage = () => {
);
};

const getRoleTeams = (teams: Array<EntityReference>) => {
const AddTeamButton = () => (
<div className="tw-flex tw-justify-end tw-mr-1">
<NonAdminAction position="bottom" title={TITLE_FOR_NON_ADMIN_ACTION}>
<Button
className={classNames('tw-h-8 tw-rounded tw-mb-3', {
'tw-opacity-40': !isAdminUser && !isAuthDisabled,
})}
data-testid="add-teams-button"
size="small"
theme="primary"
variant="contained"
onClick={() => setIsAddingTeams(true)}>
Add teams
</Button>
</NonAdminAction>
</div>
);
if (!teams.length) {
return (
<div className="tw-text-center tw-py-5">
<AddTeamButton />
<p className="tw-text-base">No Teams Added.</p>
</div>
);
}

return (
<Fragment>
<div className="tw-flex tw-justify-between">
<p>
{currentRole?.displayName ?? currentRole?.name} role is assigned to
all users who are part of following teams.
</p>
<AddTeamButton />
</div>
<div
className="tw-grid xxl:tw-grid-cols-4 md:tw-grid-cols-3 tw-gap-4"
data-testid="teams-card">
{teams.map((team, i) => {
const teamData = {
description: team.displayName || team.name || '',
name: team.name as string,
id: team.id,
};

return <UserCard isIconVisible item={teamData} key={i} />;
})}
</div>
</Fragment>
);
};

const fetchUserCounts = () => {
getUserCounts()
.then((res: AxiosResponse) => {
Expand All @@ -589,11 +692,23 @@ const RolesPage = () => {
});
};

const getUniqueTeamList = () => {
const currentRoleTeams = currentRole?.teams ?? [];

return teamList.filter(
(team) => !currentRoleTeams.some((cTeam) => cTeam.id === team.id)
);
};

useEffect(() => {
fetchRoles();
fetchUserCounts();
}, []);

useEffect(() => {
setTeamList(AppState.userTeams as Team[]);
}, [AppState.userTeams]);

useEffect(() => {
if (currentRole) {
fetchPolicy(currentRole?.policy?.id as string);
Expand Down Expand Up @@ -690,6 +805,9 @@ const RolesPage = () => {
</Fragment>
) : null}
{currentTab === 2
? getRoleTeams(currentRole?.teams ?? [])
: null}
{currentTab === 3
? getRoleUsers(currentRole?.users ?? [])
: null}
</>
Expand Down Expand Up @@ -792,6 +910,17 @@ const RolesPage = () => {
onConfirm={onSetDefaultRole}
/>
)}
{isAddingTeams && (
<AddUsersModal
header={`Adding teams to ${
currentRole?.displayName ?? currentRole?.name
} role`}
list={getUniqueTeamList() as EntityReference[]}
searchPlaceHolder="Search for teams..."
onCancel={() => setIsAddingTeams(false)}
onSave={(data) => addTeams(data as Team[])}
/>
)}
</div>
)}
</PageLayout>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,26 +128,32 @@ describe('Test RolesPage component', () => {
expect(headerTitle.textContent).toBe(mockGetRole.data[0].displayName);
expect(addNewRuleButton).toBeInTheDocument();
expect(tabs).toBeInTheDocument();
expect(tabs.childElementCount).toBe(2);
expect(tabs.childElementCount).toBe(3);
expect(tabs.children[0].textContent).toBe('Policy');
expect(tabs.children[1].textContent).toBe('Users');
expect(tabs.children[1].textContent).toBe('Teams');
expect(tabs.children[2].textContent).toBe('Users');
expect(tabs.children[0]).toHaveClass('active');
expect(description).toBeInTheDocument();
});

it('Check no rule and no user behaviour', async () => {
it('Check no rule, no user and no teams behaviour', async () => {
const { container } = render(<RolesPage />, {
wrapper: MemoryRouter,
});
// checking No Rules Added. directly as there is no data available on 1st instance

const usersButton = await findByTestId(container, 'users');
const teamsButton = await findByTestId(container, 'teams');

expect(await findByText(container, /No Rules Added./i)).toBeInTheDocument();

fireEvent.click(usersButton);

expect(await findByText(container, /No Users Added./i)).toBeInTheDocument();

fireEvent.click(teamsButton);

expect(await findByText(container, /No Teams Added./i)).toBeInTheDocument();
});

it('Check behaviour when there is data in policy and user', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,28 @@
* limitations under the License.
*/

import { toLower } from 'lodash';
import React, { useState } from 'react';
import { Button } from '../../components/buttons/Button/Button';
import Searchbar from '../../components/common/searchbar/Searchbar';
import { EntityReference as UserTeams } from '../../generated/entity/teams/user';
import UserCard from './UserCard';

type Props = {
searchPlaceHolder?: string;
header: string;
list: Array<UserTeams>;
onCancel: () => void;
onSave: (data: Array<UserTeams>) => void;
};

const AddUsersModal = ({ header, list, onCancel, onSave }: Props) => {
const AddUsersModal = ({
header,
list,
onCancel,
onSave,
searchPlaceHolder,
}: Props) => {
const [selectedUsers, setSelectedusers] = useState<Array<string>>([]);
const [searchText, setSearchText] = useState('');

Expand All @@ -45,13 +53,14 @@ const AddUsersModal = ({ header, list, onCancel, onSave }: Props) => {
return list
.filter((user) => {
return (
user.description?.includes(searchText) ||
user?.name?.includes(searchText)
toLower(user.description)?.includes(toLower(searchText)) ||
toLower(user.displayName)?.includes(toLower(searchText)) ||
toLower(user?.name)?.includes(toLower(searchText))
);
})
.map((user, index) => {
const User = {
description: user.description || '',
description: user.displayName || user.description || '',
name: user.name || '',
id: user.id,
};
Expand Down Expand Up @@ -91,7 +100,9 @@ const AddUsersModal = ({ header, list, onCancel, onSave }: Props) => {
</div>
<div className="tw-modal-body">
<Searchbar
placeholder="Search for user..."
placeholder={
searchPlaceHolder ? searchPlaceHolder : 'Search for user...'
}
searchValue={searchText}
typingInterval={1500}
onSearch={handleSearchAction}
Expand Down
16 changes: 15 additions & 1 deletion openmetadata-ui/src/main/resources/ui/src/pages/teams/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,21 @@ const TeamsPage = () => {
);
}

return null;
return (
<div
className="tw-grid xxl:tw-grid-cols-4 md:tw-grid-cols-3 tw-gap-4"
data-testid="teams-card">
{currentTeam?.defaultRoles?.map((role, i) => {
const roleData = {
description: role.displayName || role.name || '',
name: role.name as string,
id: role.id,
};

return <UserCard isIconVisible item={roleData} key={i} />;
})}
</div>
);
};

const fetchLeftPanel = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const getAllUsersList = (arrQueryFields = ''): void => {
};

const getAllTeams = (): void => {
getTeams().then((res: AxiosResponse) => {
getTeams('defaultRoles').then((res: AxiosResponse) => {
AppState.updateUserTeam(res.data.data);
});
};
Expand Down

0 comments on commit 875404f

Please sign in to comment.