Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Is a member of] User groups sync data #293

Merged
merged 4 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions src/components/MemberOf/MemberOfAddModalOld.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Button, DualListSelector } from "@patternfly/react-core";
import ModalWithFormLayout from "src/components/layouts/ModalWithFormLayout";
// Data types
import {
UserGroup,
UserGroupOld,
Netgroup,
Roles,
HBACRules,
Expand All @@ -26,7 +26,7 @@ interface TabData {
export interface PropsToAdd {
modalData: ModalData;
availableData:
| UserGroup[]
| UserGroupOld[]
| Netgroup[]
| Roles[]
| HBACRules[]
Expand All @@ -35,7 +35,7 @@ export interface PropsToAdd {
groupRepository: unknown[];
updateGroupRepository: (
args:
| UserGroup[]
| UserGroupOld[]
| Netgroup[]
| Roles[]
| HBACRules[]
Expand All @@ -50,7 +50,7 @@ export interface PropsToAdd {
// its variables. Just the mandatory ones ('name' and 'description') are accessible at this point.
// To display all the possible data types for all the tabs (and not only the mandatory ones)
// an extra interface 'MemberOfElement' will be defined. This will be called when assigning
// a new group instead of refering to each type (UserGroup | Netgroup | Roles | HBACRules |
// a new group instead of refering to each type (UserGroupOld | Netgroup | Roles | HBACRules |
// SudoRules | HostGroup).
interface MemberOfElement {
hostGroup?: string;
Expand Down Expand Up @@ -136,9 +136,9 @@ const MemberOfAddModal = (props: PropsToAdd) => {
optionData.description !== undefined && optionData.description,
gid: optionData.gid !== undefined && optionData.gid,
status: optionData.status !== undefined && optionData.status,
} as UserGroup);
} as UserGroupOld);
// Send updated data to table
props.updateGroupRepository(props.groupRepository as UserGroup[]);
props.updateGroupRepository(props.groupRepository as UserGroupOld[]);
}
// Netgroups
if (props.tabData.tabName === "Netgroups") {
Expand Down
2 changes: 1 addition & 1 deletion src/components/MemberOf/MemberOfDeleteModalOld.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import MemberOfDeletedGroupsTable from "src/components/MemberOf/MemberOfDeletedG
// its variables. Just the mandatory ones ('name' and 'description') are accessible at this point.
// To display all the possible data types for all the tabs (and not only the mandatory ones)
// an extra interface 'MemberOfElement' will be defined. This will be called in the 'PropsToTable'
// interface instead of each type (UserGroup | Netgroup | Roles | HBACRules | SudoRules).
// interface instead of each type (UserGroupOld | Netgroup | Roles | HBACRules | SudoRules).
interface MemberOfElement {
name: string;
gid?: string;
Expand Down
2 changes: 1 addition & 1 deletion src/components/MemberOf/MemberOfDeletedGroupsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import TableLayout from "src/components/layouts/TableLayout";
// its variables. Just the mandatory ones ('name' and 'description') are accessible at this point.
// To display all the possible data types for all the tabs (and not only the mandatory ones)
// an extra interface 'MemberOfElement' will be defined. This will be assigned in the
// 'PropsToDeleteOnTable' interface instead of each type (UserGroup | Netgroup | Roles
// 'PropsToDeleteOnTable' interface instead of each type (UserGroupOld | Netgroup | Roles
// | HBACRules | SudoRules).
interface MemberOfElement {
name: string;
Expand Down
2 changes: 1 addition & 1 deletion src/components/MemberOf/MemberOfTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ interface ColumnNames {
// its variables. Just the mandatory ones ('name' and 'description') are accessible at this point.
// To display all the possible data types for all the tabs (and not only the mandatory ones)
// an extra interface 'MemberOfElement' will be defined. This will be called in the 'PropsToTable'
// interface instead of each type (UserGroup | Netgroup | Roles | HBACRules | SudoRules | HostGroup).
// interface instead of each type (UserGroupOld | Netgroup | Roles | HBACRules | SudoRules | HostGroup).
interface MemberOfElement {
name: string;
gid?: string;
Expand Down
10 changes: 6 additions & 4 deletions src/components/MemberOf/MemberOfTableUserGroups.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { UserGroup } from "src/utils/datatypes/globalDataTypes";
// Components
import SkeletonOnTableLayout from "../layouts/Skeleton/SkeletonOnTableLayout";
import EmptyBodyTable from "../tables/EmptyBodyTable";
// Utils
import { parseEmptyString } from "src/utils/utils";

export interface MemberOfUserGroupsTableProps {
userGroups: UserGroup[];
Expand All @@ -31,13 +33,13 @@ const UserGroupsTableBody = (props: {
select={{
rowIndex: index,
onSelect: (_e, isSelected) =>
props.onCheckboxChange(isSelected, userGroup.name),
isSelected: props.checkedItems.includes(userGroup.name),
props.onCheckboxChange(isSelected, userGroup.cn),
isSelected: props.checkedItems.includes(userGroup.cn),
}}
/>
)}
<Td>{userGroup.name}</Td>
<Td>{userGroup.gid}</Td>
<Td>{userGroup.cn}</Td>
<Td>{parseEmptyString(userGroup.gidnumber)}</Td>
<Td>{userGroup.description}</Td>
</Tr>
))}
Expand Down
8 changes: 4 additions & 4 deletions src/components/MemberOf/MemberOfToolbarOld.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import ToolbarLayout, {
} from "src/components/layouts/ToolbarLayout";
// Data types
import {
UserGroup,
UserGroupOld,
Netgroup,
Roles,
HBACRules,
Expand Down Expand Up @@ -57,7 +57,7 @@ interface ButtonData {
interface SettersData {
changeMemberGroupsList: (
arg:
| UserGroup[]
| UserGroupOld[]
| Netgroup[]
| Roles[]
| HBACRules[]
Expand All @@ -74,14 +74,14 @@ interface SearchValueData {

export interface PropsToToolbar {
pageRepo:
| UserGroup[]
| UserGroupOld[]
| Netgroup[]
| Roles[]
| HBACRules[]
| SudoRules[]
| HostGroup[];
shownItems:
| UserGroup[]
| UserGroupOld[]
| Netgroup[]
| Roles[]
| HBACRules[]
Expand Down
97 changes: 65 additions & 32 deletions src/components/MemberOf/MemberOfUserGroups.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,60 +2,93 @@ import React from "react";
// PatternFly
import { Pagination, PaginationVariant } from "@patternfly/react-core";
// Data types
import { UserGroup } from "src/utils/datatypes/globalDataTypes";
// Redux
import { useAppSelector } from "src/store/hooks";
import { User, UserGroup } from "src/utils/datatypes/globalDataTypes";
// Components
import MemberOfToolbarUserGroups, {
MembershipDirection,
} from "./MemberOfToolbar";
import MemberOfUserGroupsTable from "./MemberOfTableUserGroups";
import MemberOfAddModal, { AvailableItems } from "./MemberOfAddModal";
import MemberOfDeleteModal from "./MemberOfDeleteModal";
// Hooks
import { useUserMemberOfData } from "src/hooks/useUserMemberOfData";

function paginate<Type>(array: Type[], page: number, perPage: number): Type[] {
const startIdx = (page - 1) * perPage;
const endIdx = perPage * page - 1;
return array.slice(startIdx, endIdx);
}

interface TypeWithName {
name: string;
interface TypeWithCN {
cn: string;
}

// Filter functions to compare the available data with the data that
// the user is already member of. This is done to prevent duplicates
// (e.g: adding the same element twice).
function filterUserGroupsData<Type extends TypeWithName>(
function filterUserGroupsData<Type extends TypeWithCN>(
list1: Array<Type>,
list2: Array<Type>
): Type[] {
// User groups
return list1.filter((item) => {
return !list2.some((itm) => {
return item.name === itm.name;
return item.cn === itm.cn;
});
});
}

interface MemberOfUserGroupsProps {
uid: string;
usersGroupsFromUser: UserGroup[];
updateUsersGroupsFromUser: (newList: UserGroup[]) => void;
user: Partial<User>;
}

const MemberOfUserGroups = (props: MemberOfUserGroupsProps) => {
const userGroupsFullList = useAppSelector(
(state) => state.usergroups.userGroupList
);

const [groupsNamesSelected, setGroupsNamesSelected] = React.useState<
string[]
// 'User groups' assigned to user
const [userGroupsFromUser, setUserGroupsFromUser] = React.useState<
UserGroup[]
>([]);

// Page indexes
const [page, setPage] = React.useState(1);
const [perPage, setPerPage] = React.useState(10);

const firstUserIdx = (page - 1) * perPage;
const lastUserIdx = page * perPage;

const uid = props.user.uid;

// API call: full list of 'User groups' available
const fullUserGroupsQuery = useUserMemberOfData({
uid,
firstUserIdx,
lastUserIdx,
});

const userGroupsFullList = fullUserGroupsQuery.userGroupsFullList;

// Get full data of the 'User groups' assigned to user
React.useEffect(() => {
if (!fullUserGroupsQuery.isFetching && userGroupsFullList) {
const userGroupsParsed: UserGroup[] = [];
props.user.memberof_group?.map((group) => {
userGroupsFullList.map((g) => {
if (g.cn === group) {
userGroupsParsed.push(g);
}
});
});
if (
JSON.stringify(userGroupsFromUser) !== JSON.stringify(userGroupsParsed)
) {
setUserGroupsFromUser(userGroupsParsed);
}
}
}, [fullUserGroupsQuery]);

const [groupsNamesSelected, setGroupsNamesSelected] = React.useState<
string[]
>([]);

const [searchValue, setSearchValue] = React.useState("");

const [membershipDirection, setMembershipDirection] =
Expand All @@ -66,22 +99,22 @@ const MemberOfUserGroups = (props: MemberOfUserGroupsProps) => {

// Computed "states"
const someItemSelected = groupsNamesSelected.length > 0;
const shownUserGroups = paginate(props.usersGroupsFromUser, page, perPage);
const showTableRows = props.usersGroupsFromUser.length > 0;
const shownUserGroups = paginate(userGroupsFromUser, page, perPage);
const showTableRows = userGroupsFromUser.length > 0;
pvoborni marked this conversation as resolved.
Show resolved Hide resolved

// Available data to be added as member of
const userGroupsFilteredData: UserGroup[] = filterUserGroupsData(
userGroupsFullList,
props.usersGroupsFromUser
userGroupsFromUser
);

// Parse availableItems to AvailableItems type
const parseAvailableItems = () => {
const avItems: AvailableItems[] = [];
userGroupsFilteredData.map((item) => {
avItems.push({
key: item.name,
title: item.name,
key: item.cn,
title: item.cn,
});
});
return avItems;
Expand All @@ -93,18 +126,18 @@ const MemberOfUserGroups = (props: MemberOfUserGroupsProps) => {
const onAddUserGroup = (items: AvailableItems[]) => {
const newItems = items.map((item) => item.key);
const newGroups = userGroupsFullList.filter((group) =>
newItems.includes(group.name)
newItems.includes(group.cn)
);
const updatedGroups = props.usersGroupsFromUser.concat(newGroups);
props.updateUsersGroupsFromUser(updatedGroups);
const updatedGroups = userGroupsFromUser.concat(newGroups);
setUserGroupsFromUser(updatedGroups);
};

// 'Delete' function
const onDeleteUserGroup = () => {
const updatedGroups = props.usersGroupsFromUser.filter(
(group) => !groupsNamesSelected.includes(group.name)
const updatedGroups = userGroupsFromUser.filter(
(group) => !groupsNamesSelected.includes(group.cn)
);
props.updateUsersGroupsFromUser(updatedGroups);
setUserGroupsFromUser(updatedGroups);
};

return (
Expand All @@ -121,7 +154,7 @@ const MemberOfUserGroups = (props: MemberOfUserGroupsProps) => {
membershipDirection={membershipDirection}
onMembershipDirectionChange={setMembershipDirection}
helpIconEnabled={true}
totalItems={props.usersGroupsFromUser.length}
totalItems={userGroupsFromUser.length}
perPage={perPage}
page={page}
onPerPageChange={setPerPage}
Expand All @@ -135,7 +168,7 @@ const MemberOfUserGroups = (props: MemberOfUserGroupsProps) => {
/>
<Pagination
className="pf-v5-u-pb-0 pf-v5-u-pr-md"
itemCount={props.usersGroupsFromUser.length}
itemCount={userGroupsFromUser.length}
widgetId="pagination-options-menu-bottom"
perPage={perPage}
page={page}
Expand All @@ -150,7 +183,7 @@ const MemberOfUserGroups = (props: MemberOfUserGroupsProps) => {
availableItems={availableUserGroupsItems}
onAdd={onAddUserGroup}
onSearchTextChange={setSearchValue}
title={"Add '" + props.uid + "' into User groups"}
title={"Add '" + props.user.uid + "' into User groups"}
ariaLabel="Add user of user group modal"
/>
)}
Expand All @@ -163,8 +196,8 @@ const MemberOfUserGroups = (props: MemberOfUserGroupsProps) => {
>
<MemberOfUserGroupsTable
userGroups={
props.usersGroupsFromUser.filter((group) =>
groupsNamesSelected.includes(group.name)
userGroupsFromUser.filter((group) =>
groupsNamesSelected.includes(group.cn)
) as UserGroup[]
}
showTableRows
Expand Down
Loading
Loading