Skip to content

Commit

Permalink
Implement 'Revert' and 'Save' buttons
Browse files Browse the repository at this point in the history
Signed-off-by: Carla Martinez <[email protected]>
  • Loading branch information
carma12 committed Jul 24, 2023
1 parent 524c38f commit 11496ba
Show file tree
Hide file tree
Showing 8 changed files with 199 additions and 8 deletions.
64 changes: 61 additions & 3 deletions src/components/UserSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,13 @@ import UsersContactSettings from "src/components/UsersSections/UsersContactSetti
import UsersMailingAddress from "src/components/UsersSections/UsersMailingAddress";
import UsersEmployeeInfo from "src/components/UsersSections/UsersEmployeeInfo";
import UsersAttributesSMB from "src/components/UsersSections/UsersAttributesSMB";
// RPC
import { useSaveUserMutation } from "src/services/rpc";
// Hooks
import useAlerts from "src/hooks/useAlerts";

export interface PropsToUserSettings {
originalUser: Partial<User>;
user: Partial<User>; // TODO: Replace with `userData` in all subsections
onUserChange: (user: Partial<User>) => void;
metadata: Metadata;
Expand All @@ -43,12 +48,21 @@ export interface PropsToUserSettings {
krbPolicyData: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
certData: any;
onRefresh?: () => void;
onRefresh: () => void;
isModified: boolean;
isDataLoading?: boolean;
modifiedValues: () => Partial<User>;
onResetValues: () => void;
from: "active-users" | "stage-users" | "preserved-users";
}

const UserSettings = (props: PropsToUserSettings) => {
// Alerts to show in the UI
const alerts = useAlerts();

// RTK hook: save user
const [saveUser] = useSaveUserMutation();

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

Expand Down Expand Up @@ -80,6 +94,38 @@ const UserSettings = (props: PropsToUserSettings) => {
setIsKebabOpen(!isKebabOpen);
};

// 'Revert' handler method
const onRevert = () => {
props.onUserChange(props.originalUser);
alerts.addAlert("revert-success", "User data reverted", "success");
};

// 'Save' handler method
const onSave = () => {
const modifiedValues = props.modifiedValues();
// Assign uid
modifiedValues.uid = props.user.uid;

// Make API call
saveUser(modifiedValues).then((response) => {
if ("data" in response) {
if (response.data.result) {
// Show toast notification: success
alerts.addAlert("save-success", "User modified", "success");
} else if (response.data.error) {
// Show toast notification: error
alerts.addAlert(
"save-error",
response.data.error || "Error when modifying user",
"danger"
);
}
// TODO: Reset values. Disable 'revert' and 'save' buttons
props.onResetValues();
}
});
};

// Toolbar
const toolbarFields = [
{
Expand All @@ -92,11 +138,22 @@ const UserSettings = (props: PropsToUserSettings) => {
},
{
key: 1,
element: <SecondaryButton isDisabled={true}>Revert</SecondaryButton>,
element: (
<SecondaryButton
isDisabled={!props.isModified}
onClickHandler={onRevert}
>
Revert
</SecondaryButton>
),
},
{
key: 2,
element: <SecondaryButton isDisabled={true}>Save</SecondaryButton>,
element: (
<SecondaryButton isDisabled={!props.isModified} onClickHandler={onSave}>
Save
</SecondaryButton>
),
},
{
key: 3,
Expand All @@ -117,6 +174,7 @@ const UserSettings = (props: PropsToUserSettings) => {
// 'UserSettings' render
return (
<>
<alerts.ManagedAlerts />
<PageSection
id="settings-page"
variant={PageSectionVariants.light}
Expand Down
35 changes: 31 additions & 4 deletions src/hooks/useUserSettingsData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@ type UserSettingsData = {
isLoading: boolean;
isFetching: boolean;
modified: boolean;
resetValues: () => void;
metadata: Metadata;
originalUser?: Partial<User>;
originalUser: Partial<User>;
user: Partial<User>;
setUser: (user: Partial<User>) => void;
pwPolicyData?: Record<string, unknown>;
krbtPolicyData?: Record<string, unknown>;
certData?: Record<string, unknown>;
refetch?: () => void;
refetch: () => void;
modifiedValues: () => Partial<User>;
};

const useUserSettingsData = (userId: string): UserSettingsData => {
Expand All @@ -32,6 +34,8 @@ const useUserSettingsData = (userId: string): UserSettingsData => {
const userFullData = userFullDataQuery.data;
const isFullDataLoading = userFullDataQuery.isLoading;

const [modified, setModified] = useState(false);

// Data displayed and modified by the user
const [user, setUser] = useState<Partial<User>>({});
useEffect(() => {
Expand All @@ -43,19 +47,37 @@ const useUserSettingsData = (userId: string): UserSettingsData => {
const settingsData = {
isLoading: metadataLoading || isFullDataLoading,
isFetching: userFullDataQuery.isFetching,
modified,
metadata,
user,
setUser,
refetch: userFullDataQuery.refetch,
} as UserSettingsData;

if (userFullData) {
settingsData.originalUser = userFullData.user;
settingsData.originalUser = userFullData.user || {};
settingsData.pwPolicyData = userFullData.pwPolicy;
settingsData.krbtPolicyData = userFullData.krbtPolicy;
settingsData.certData = userFullData.cert;
} else {
settingsData.originalUser = {};
}

const getModifiedValues = (): Partial<User> => {
if (!userFullData || !userFullData.user) {
return {};
}

const modifiedValues = {};
for (const [key, value] of Object.entries(user)) {
if (userFullData.user[key] !== value) {
modifiedValues[key] = value;
}
}
return modifiedValues;
};
settingsData.modifiedValues = getModifiedValues;

useEffect(() => {
if (!userFullData || !userFullData.user) {
return;
Expand All @@ -67,9 +89,14 @@ const useUserSettingsData = (userId: string): UserSettingsData => {
break;
}
}
settingsData.modified = modified;
setModified(modified);
}, [user, userFullData]);

const onResetValues = () => {
setModified(false);
};
settingsData.resetValues = onResetValues;

return settingsData;
};

Expand Down
4 changes: 4 additions & 0 deletions src/pages/ActiveUsers/ActiveUsersTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ const ActiveUsersTabs = () => {
>
<PageSection className="pf-u-pb-0"></PageSection>
<UserSettings
originalUser={userSettingsData.originalUser}
user={userSettingsData.user}
metadata={userSettingsData.metadata}
pwPolicyData={userSettingsData.pwPolicyData}
Expand All @@ -95,6 +96,9 @@ const ActiveUsersTabs = () => {
onUserChange={userSettingsData.setUser}
isDataLoading={userSettingsData.isFetching}
onRefresh={userSettingsData.refetch}
isModified={userSettingsData.modified}
onResetValues={userSettingsData.resetValues}
modifiedValues={userSettingsData.modifiedValues}
from="active-users"
/>
</Tab>
Expand Down
6 changes: 6 additions & 0 deletions src/pages/PreservedUsers/PreservedUsersTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,18 @@ const PreservedUsersTabs = () => {
>
<PageSection className="pf-u-pb-0"></PageSection>
<UserSettings
originalUser={userSettingsData.originalUser}
user={userSettingsData.user}
metadata={userSettingsData.metadata}
pwPolicyData={userSettingsData.pwPolicyData}
krbPolicyData={userSettingsData.krbtPolicyData}
certData={userSettingsData.certData}
onUserChange={userSettingsData.setUser}
isDataLoading={userSettingsData.isFetching}
onRefresh={userSettingsData.refetch}
isModified={userSettingsData.modified}
onResetValues={userSettingsData.resetValues}
modifiedValues={userSettingsData.modifiedValues}
from="preserved-users"
/>
</Tab>
Expand Down
6 changes: 6 additions & 0 deletions src/pages/StageUsers/StageUsersTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,18 @@ const StageUsersTabs = () => {
>
<PageSection className="pf-u-pb-0"></PageSection>
<UserSettings
originalUser={userSettingsData.originalUser}
user={userSettingsData.user}
metadata={userSettingsData.metadata}
pwPolicyData={userSettingsData.pwPolicyData}
krbPolicyData={userSettingsData.krbtPolicyData}
certData={userSettingsData.certData}
onUserChange={userSettingsData.setUser}
isDataLoading={userSettingsData.isFetching}
onRefresh={userSettingsData.refetch}
isModified={userSettingsData.modified}
onResetValues={userSettingsData.resetValues}
modifiedValues={userSettingsData.modifiedValues}
from="stage-users"
/>
</Tab>
Expand Down
20 changes: 19 additions & 1 deletion src/services/rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
// Utils
import { API_VERSION_BACKUP } from "src/utils/utils";
import { Metadata, User } from "src/utils/datatypes/globalDataTypes";
import { apiToUser } from "src/utils/userUtils";

export type UserFullData = {
user?: Partial<User>;
Expand Down Expand Up @@ -271,14 +272,30 @@ export const api = createApi({
transformResponse: (response: BatchResponse): UserFullData => {
const results = response.result.results;
return {
user: results[0].result,
user: apiToUser(results[0].result),
pwPolicy: results[1].result,
krbtPolicy: results[2].result,
cert: results[3].result,
};
},
providesTags: ["FullUserData"],
}),
saveUser: build.mutation<FindRPCResponse, Partial<User>>({
query: (user) => {
const params = {
version: API_VERSION_BACKUP,
...user,
};

delete params["uid"];

return getCommand({
method: "user_mod",
params: [[user.uid], params],
});
},
invalidatesTags: ["FullUserData"],
}),
}),
});

Expand All @@ -290,4 +307,5 @@ export const {
useGettingUserDataQuery,
useGetObjectMetadataQuery,
useGetUsersFullDataQuery,
useSaveUserMutation,
} = api;
19 changes: 19 additions & 0 deletions src/utils/ipaObjectUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,3 +166,22 @@ export function convertToString(value: BasicType): string {
return String(value);
}
}

export function convertApiObj(
apiRecord: Record<string, unknown>,
simpleValues: Set<string>,
dateValues: Set<string>
) {
const obj = {};
for (const [key, value] of Object.entries(apiRecord)) {
if (simpleValues.has(key)) {
obj[key] = convertToString(value as BasicType);
} else if (dateValues.has(key)) {
// TODO convert to Datetime object
obj[key] = value;
} else {
obj[key] = value;
}
}
return obj;
}
53 changes: 53 additions & 0 deletions src/utils/userUtils.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Data types
import { User } from "src/utils/datatypes/globalDataTypes";
import { convertApiObj } from "src/utils/ipaObjectUtils";

// Parse the 'textInputField' data into expected data type
// - TODO: Adapt it to work with many types of data
Expand All @@ -18,3 +19,55 @@ export const asRecord = (

return { ipaObject, recordOnChange };
};

const simpleValues = new Set([
"title",
"givenname",
"sn",
"cn",
"displayname",
"initials",
"gecos",
"userclass",
"uid",
"uidnumber",
"gidnumber",
"loginshell",
"homedirectory",
"ipatokenradiusconfiglink",
"ipatokenradiususername",
"ipaidpconfiglink",
"ipaidpsub",
"krbmaxpwdlife",
"krbminpwdlife",
"krbpwdhistorylength",
"krbpwdmindiffchars",
"krbpwdminlength",
"krbpwdmaxfailure",
"krbpwdfailurecountinterval",
"krbpwdlockoutduration",
"passwordgracelimit",
"krbmaxrenewableage",
"krbmaxticketlife",
"street",
"l",
"st",
"postalcode",
"ou",
"manager",
"employeenumber",
"employeetype",
"preferredlanguage",
"ipantlogonscript",
"ipantprofilepath",
"ipanthomedirectory",
"ipanthomedirectorydrive",
"ipauniqueid",
"ipantsecurityidentifier",
"dn",
]);
const dateValues = new Set(["krbpasswordexpiration", "krbprincipalexpiration"]);

export function apiToUser(apiRecord: Record<string, unknown>) {
return convertApiObj(apiRecord, simpleValues, dateValues) as Partial<User>;
}

0 comments on commit 11496ba

Please sign in to comment.