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 19, 2024
1 parent acf201e commit b2c9bf9
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 138 deletions.
18 changes: 17 additions & 1 deletion src/components/UserSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import ResetPassword from "./modals/ResetPassword";
import IssueNewCertificate from "./modals/IssueNewCertificate";
import AddOtpToken from "./modals/AddOtpToken";
import ActivateStageUsers from "./modals/ActivateStageUsers";
import RestorePreservedUsers from "./modals/RestorePreservedUsers";
// Utils
import { API_VERSION_BACKUP } from "src/utils/utils";
// Navigate
Expand Down Expand Up @@ -246,6 +247,12 @@ const UserSettings = (props: PropsToUserSettings) => {
setIsActivateModalOpen(false);
};

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

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

Expand Down Expand Up @@ -318,7 +325,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 @@ -624,6 +633,13 @@ const UserSettings = (props: PropsToUserSettings) => {
selectedUids={selectedUsers}
onSuccess={() => navigate(URL_PREFIX + "/stage-users")}
/>
<RestorePreservedUsers
show={isRestoreModalOpen}
handleModalToggle={onCloseRestoreModal}
selectedUids={selectedUsers}
updateSelectedUids={setSelectedUsers}
onSuccess={() => navigate(URL_PREFIX + "/preserved-users")}
/>
</>
);
};
Expand Down
175 changes: 43 additions & 132 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,30 +14,16 @@ 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";

interface SelectedUsersData {
selectedUsers: string[];
updateSelectedUsers: (newSelectedUsers: string[]) => void;
}

export interface PropsToPreservedUsers {
show: boolean;
handleModalToggle: () => void;
selectedUsersData: SelectedUsersData;
onRefresh?: () => void;
selectedUids: string[];
updateSelectedUids: (newSelectedUids: string[]) => void;
onSuccess: () => void;
}

const RestorePreservedUsers = (props: PropsToPreservedUsers) => {
Expand All @@ -48,7 +34,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 All @@ -66,7 +52,7 @@ const RestorePreservedUsers = (props: PropsToPreservedUsers) => {
id: "restore-users-table",
pfComponent: (
<UsersDisplayTable
usersToDisplay={props.selectedUsersData.selectedUsers}
usersToDisplay={props.selectedUids}
from={"preserved-users"}
/>
),
Expand All @@ -78,110 +64,48 @@ 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.selectedUids;

// [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.selectedUids.map((user) => {
dispatch(removePreservedUser(user[0]));
});

// Reset selected values
props.updateSelectedUids([]);

// 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 or navigate
props.onSuccess();
} else if (error) {
// Handle error
handleAPIError(error);
alerts.addAlert("restore-users-error", error, "danger");
}
}
setBtnSpinning(false);
Expand All @@ -207,33 +131,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
21 changes: 18 additions & 3 deletions src/components/modals/StagePreservedUsers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,24 @@ import ErrorModal from "./ErrorModal";
import { ErrorData } from "src/utils/datatypes/globalDataTypes";
// Hooks
import useAlerts from "src/hooks/useAlerts";
import { PropsToPreservedUsers } from "./RestorePreservedUsers";

const StagePreservedUsers = (props: PropsToPreservedUsers) => {
// Navigation
import { NavigateFunction } from "react-router-dom";

interface SelectedUsersData {
selectedUsers: string[];
updateSelectedUsers: (newSelectedUsers: string[]) => void;
}

interface PropsToStagePreservedUsers {
show: boolean;
handleModalToggle: () => void;
selectedUsersData: SelectedUsersData;
onRefresh?: () => void;
navigateFunc?: NavigateFunction;
navigateTo?: string;
}

const StagePreservedUsers = (props: PropsToStagePreservedUsers) => {
// Set dispatch (Redux)
const dispatch = useAppDispatch();

Expand Down
5 changes: 3 additions & 2 deletions src/pages/PreservedUsers/PreservedUsers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -496,8 +496,9 @@ const PreservedUsers = () => {
<RestorePreservedUsers
show={showRestoreModal}
handleModalToggle={onRestoreModalToggle}
selectedUsersData={selectedUsersData}
onRefresh={refreshUsersData}
selectedUids={selectedUsers.map((uid) => uid[0])}
updateSelectedUids={updateSelectedUsers}
onSuccess={refreshUsersData}
/>
<StagePreservedUsers
show={showStageModal}
Expand Down
17 changes: 17 additions & 0 deletions src/services/rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,22 @@ export const api = createApi({
});
});

return getBatchCommand(
batchPayload,
query_args["version"] || API_VERSION_BACKUP
);
},
}),
restoreUser: build.mutation<BatchRPCResponse, string[]>({
query: (query_args) => {
const batchPayload: Command[] = [];
query_args.map((uid) => {
batchPayload.push({
method: "user_undel",
params: [[uid], {}],
});
});

return getBatchCommand(
batchPayload,
query_args["version"] || API_VERSION_BACKUP
Expand Down Expand Up @@ -859,4 +875,5 @@ export const {
useAddOtpTokenMutation,
useGenerateSubIdsMutation,
useActivateUserMutation,
useRestoreUserMutation,
} = api;

0 comments on commit b2c9bf9

Please sign in to comment.