Skip to content

Commit

Permalink
Add 'Restore' option ('Preserved users')
Browse files Browse the repository at this point in the history
The 'Restore' options moves a given user
from 'Preserved' to 'Active' state.

The `RestorePreservedUsers` component has
been optimized for simplification, removing
the `ErrorModal` component and relying the
output messages on the `Alert` one.

Also, a new endpoint `useRestoreUserMutation`
has been created to be used instead of the
generalistic one (`useSimpleMutCommandMutation`).

Signed-off-by: Carla Martinez <carlmart@redhat.com>
  • Loading branch information
carma12 committed Jan 10, 2024
1 parent 3313efe commit 3678395
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 127 deletions.
24 changes: 23 additions & 1 deletion src/components/UserSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ import DisableEnableUsers from "./modals/DisableEnableUsers";
import DeleteUsers from "./modals/DeleteUsers";
import RebuildAutoMembership from "./modals/RebuildAutoMembership";
import UnlockUser from "./modals/UnlockUser";
import RestorePreservedUsers from "./modals/RestorePreservedUsers";
// Navigation
import { useNavigate } from "react-router-dom";
import { URL_PREFIX } from "src/navigation/NavRoutes";

export interface PropsToUserSettings {
originalUser: Partial<User>;
Expand All @@ -76,6 +80,9 @@ const UserSettings = (props: PropsToUserSettings) => {
// Alerts to show in the UI
const alerts = useAlerts();

// Navigate
const navigate = useNavigate();

// RTK hook: save user (acive/preserved and stage)
let [saveUser] = useSaveUserMutation();
if (props.from === "stage-users") {
Expand Down Expand Up @@ -160,6 +167,12 @@ const UserSettings = (props: PropsToUserSettings) => {
return isLocked;
};

// Preserved users - 'Restore' option
const [isRestoreModalOpen, setIsRestoreModalOpen] = React.useState(false);
const onCloseRestoreModal = () => {
setIsRestoreModalOpen(false);
};

// Kebab
const [isKebabOpen, setIsKebabOpen] = useState(false);

Expand Down Expand Up @@ -208,7 +221,9 @@ const UserSettings = (props: PropsToUserSettings) => {

const preservedDropdownItems = [
<DropdownItem key="stage">Stage</DropdownItem>,
<DropdownItem key="restore">Restore</DropdownItem>,
<DropdownItem key="restore" onClick={() => setIsRestoreModalOpen(true)}>
Restore
</DropdownItem>,
<DropdownItem key="delete" onClick={() => setIsDeleteModalOpen(true)}>
Delete
</DropdownItem>,
Expand Down Expand Up @@ -488,6 +503,13 @@ const UserSettings = (props: PropsToUserSettings) => {
entriesToRebuild={userToRebuild}
entity="users"
/>
<RestorePreservedUsers
show={isRestoreModalOpen}
handleModalToggle={onCloseRestoreModal}
selectedUsersData={selectedUsersData}
navigateFunc={navigate}
navigateTo={URL_PREFIX + "/preserved-users"}
/>
</>
);
};
Expand Down
177 changes: 53 additions & 124 deletions src/components/modals/RestorePreservedUsers.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from "react";
import React from "react";
// PatternFly
import {
Button,
Expand All @@ -14,19 +14,11 @@ import UsersDisplayTable from "src/components/tables/UsersDisplayTable";
import { useAppDispatch } from "src/store/hooks";
import { removeUser as removePreservedUser } from "src/store/Identity/preservedUsers-slice";
// RPC
import {
Command,
BatchRPCResponse,
useBatchMutCommandMutation,
} from "src/services/rpc";
import { FetchBaseQueryError } from "@reduxjs/toolkit/dist/query";
import { SerializedError } from "@reduxjs/toolkit";
// Modals
import ErrorModal from "./ErrorModal";
// Data types
import { ErrorData } from "src/utils/datatypes/globalDataTypes";
import { BatchRPCResponse, useRestoreUserMutation } from "src/services/rpc";
// Hooks
import useAlerts from "src/hooks/useAlerts";
// Navigation
import { NavigateFunction } from "react-router-dom";

interface SelectedUsersData {
selectedUsers: string[];
Expand All @@ -38,6 +30,8 @@ export interface PropsToPreservedUsers {
handleModalToggle: () => void;
selectedUsersData: SelectedUsersData;
onRefresh?: () => void;
navigateFunc?: NavigateFunction;
navigateTo?: string;
}

const RestorePreservedUsers = (props: PropsToPreservedUsers) => {
Expand All @@ -48,7 +42,7 @@ const RestorePreservedUsers = (props: PropsToPreservedUsers) => {
const alerts = useAlerts();

// Define 'executeUserRestoreCommand' to restore a preserved user
const [executeUserRestoreCommand] = useBatchMutCommandMutation();
const [executeUserRestoreCommand] = useRestoreUserMutation();

// List of fields
const fields = [
Expand Down Expand Up @@ -78,110 +72,58 @@ const RestorePreservedUsers = (props: PropsToPreservedUsers) => {
props.handleModalToggle();
};

// Handle API error data
const [isModalErrorOpen, setIsModalErrorOpen] = useState(false);
const [errorTitle, setErrorTitle] = useState("");
const [errorMessage, setErrorMessage] = useState("");

const closeAndCleanErrorParameters = () => {
setIsModalErrorOpen(false);
setErrorTitle("");
setErrorMessage("");
};

const onCloseErrorModal = () => {
closeAndCleanErrorParameters();
};

const errorModalActions = [
<Button key="cancel" variant="link" onClick={onCloseErrorModal}>
OK
</Button>,
];

const handleAPIError = (error: FetchBaseQueryError | SerializedError) => {
if ("code" in error) {
setErrorTitle("IPA error " + error.code + ": " + error.name);
if (error.message !== undefined) {
setErrorMessage(error.message);
}
} else if ("data" in error) {
const errorData = error.data as ErrorData;
const errorCode = errorData.code as string;
const errorName = errorData.name as string;
const errorMessage = errorData.error as string;

setErrorTitle("IPA error " + errorCode + ": " + errorName);
setErrorMessage(errorMessage);
}
setIsModalErrorOpen(true);
};

const [spinning, setBtnSpinning] = React.useState<boolean>(false);

// Restore preserved user
const restoreUsers = () => {
// Prepare user params
const uidsToRestorePayload: Command[] = [];

setBtnSpinning(true);

props.selectedUsersData.selectedUsers.map((uid) => {
const payloadItem = {
method: "user_undel",
params: [uid, {}],
} as Command;
uidsToRestorePayload.push(payloadItem);
});
// Prepare users params
const uidsToRestorePayload = props.selectedUsersData.selectedUsers;

// [API call] Restore elements
executeUserRestoreCommand(uidsToRestorePayload).then((response) => {
if ("data" in response) {
const data = response.data as BatchRPCResponse;
const result = data.result;
const error = data.error as FetchBaseQueryError | SerializedError;
const error = data.error;

if (result) {
if ("error" in result.results[0] && result.results[0].error) {
const errorData = {
code: result.results[0].error_code,
name: result.results[0].error_name,
error: result.results[0].error,
} as ErrorData;

const error = {
status: "CUSTOM_ERROR",
data: errorData,
} as FetchBaseQueryError;

// Handle error
handleAPIError(error);
} else {
// Update data from Redux
props.selectedUsersData.selectedUsers.map((user) => {
dispatch(removePreservedUser(user[0]));
});

// Reset selected values
props.selectedUsersData.updateSelectedUsers([]);

// Refresh data
if (props.onRefresh !== undefined) {
props.onRefresh();
}

// Show alert: success
alerts.addAlert(
"restore-users-success",
"Users restored",
"success"
);

closeModal();
// Close modal
closeModal();

// Update data from Redux
props.selectedUsersData.selectedUsers.map((user) => {
dispatch(removePreservedUser(user[0]));
});

// Reset selected values
props.selectedUsersData.updateSelectedUsers([]);

// Show alert: success
let successMessage = "";
if (result.count > 1) {
successMessage = result.count + " users restored";
} else if (result.count === 1) {
successMessage = result.results[0].summary;
}
alerts.addAlert("restore-users-success", successMessage, "success");

// Refresh data
if (props.onRefresh !== undefined) {
props.onRefresh();
}

// Navigate to selected page
if (
props.navigateFunc !== undefined &&
props.navigateTo !== undefined
) {
props.navigateFunc(props.navigateTo);
}
} else if (error) {
// Handle error
handleAPIError(error);
alerts.addAlert("restore-users-error", error, "danger");
}
}
setBtnSpinning(false);
Expand All @@ -207,33 +149,20 @@ const RestorePreservedUsers = (props: PropsToPreservedUsers) => {
</Button>,
];

const modalRestore: JSX.Element = (
<ModalWithFormLayout
variantType="medium"
modalPosition="top"
offPosition="76px"
title="Restore preserved user"
formId="restore-users-stage-modal"
fields={fields}
show={props.show}
onClose={closeModal}
actions={modalRestoreActions}
/>
);

return (
<>
<alerts.ManagedAlerts />
{modalRestore}
{isModalErrorOpen && (
<ErrorModal
title={errorTitle}
isOpen={isModalErrorOpen}
onClose={onCloseErrorModal}
actions={errorModalActions}
errorMessage={errorMessage}
/>
)}
<ModalWithFormLayout
variantType="medium"
modalPosition="top"
offPosition="76px"
title="Restore preserved user"
formId="restore-users-stage-modal"
fields={fields}
show={props.show}
onClose={closeModal}
actions={modalRestoreActions}
/>
</>
);
};
Expand Down
4 changes: 2 additions & 2 deletions src/components/tables/UsersDisplayTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const UsersDisplayTable = (props: PropsToDisplayUsersTable) => {
case "stage-users":
stageUsersListCopy.map((user) => {
props.usersToDisplay.map((selected) => {
if (user.uid === selected || user.uid[0] === selected) {
if (user.uid[0] === selected[0] || user.uid[0] === selected) {
usersToDisplay.push(user);
}
});
Expand All @@ -63,7 +63,7 @@ const UsersDisplayTable = (props: PropsToDisplayUsersTable) => {
case "preserved-users":
preservedUsersListCopy.map((user) => {
props.usersToDisplay.map((selected) => {
if (user.uid === selected || user.uid[0] === selected) {
if (user.uid[0] === selected[0] || user.uid[0] === selected) {
usersToDisplay.push(user);
}
});
Expand Down
24 changes: 24 additions & 0 deletions src/services/rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,29 @@ export const api = createApi({
});
},
}),
restoreUser: build.mutation<BatchRPCResponse, any[]>({
query: (query_args) => {
const batchPayload: Command[] = [];
query_args.map((userToRestore) => {
let individualUserParams;
if (Array.isArray(userToRestore)) {
individualUserParams = [userToRestore, {}];
} else {
individualUserParams = [[userToRestore], {}];
}

batchPayload.push({
method: "user_undel",
params: individualUserParams,
});
});

return getBatchCommand(
batchPayload,
query_args["version"] || API_VERSION_BACKUP
);
},
}),
}),
});

Expand Down Expand Up @@ -782,4 +805,5 @@ export const {
useEnableUserMutation,
useDisableUserMutation,
useUnlockUserMutation,
useRestoreUserMutation,
} = api;

0 comments on commit 3678395

Please sign in to comment.