Skip to content

Commit

Permalink
ui - use member comment as justification when approving self review r…
Browse files Browse the repository at this point in the history
…oles and grops (#2849)

Signed-off-by: aporss <[email protected]>
  • Loading branch information
ArtjomsPorss authored Jan 16, 2025
1 parent 7285193 commit 08a1d28
Show file tree
Hide file tree
Showing 10 changed files with 190 additions and 702 deletions.
701 changes: 7 additions & 694 deletions ui/src/__tests__/pages/workflow/__snapshots__/admin.test.js.snap

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -1407,10 +1407,10 @@ exports[`PendingApprovalPage should render 1`] = `
width="25px"
>
<title>
notification-solid
notification
</title>
<path
d="M925.867 627.2l-68.267-68.267c-15.132-16.256-24.706-37.864-25.595-61.692l-0.005-0.174v-134.4c0-172.8-115.2-320-320-320s-320 142.933-320 320v149.333c-0.424 14.075-6.029 26.765-14.962 36.297l0.028-0.030-78.933 78.933c-7.763 7.612-12.628 18.155-12.8 29.835l-0 0.032v153.6c0 23.564 19.103 42.667 42.667 42.667v0h236.8c12.454 70.953 73.611 124.182 147.2 124.182s134.746-53.229 147.069-123.285l0.131-0.897h236.8c23.564 0 42.667-19.103 42.667-42.667v0-153.6c-0.172-11.712-5.037-22.255-12.793-29.86l-0.007-0.007z"
d="M925.867 627.2l-68.267-68.267c-15.132-16.256-24.706-37.864-25.595-61.692l-0.005-0.174v-134.4c0-172.8-115.2-320-320-320s-320 142.933-320 320v149.333c-0.424 14.075-6.029 26.765-14.962 36.297l0.028-0.030-78.933 78.933c-7.763 7.612-12.628 18.155-12.8 29.835l-0 0.032v153.6c0 23.564 19.103 42.667 42.667 42.667v0h236.8c12.454 70.953 73.611 124.182 147.2 124.182s134.746-53.229 147.069-123.285l0.131-0.897h236.8c23.564 0 42.667-19.103 42.667-42.667v0-153.6c-0.172-11.712-5.037-22.255-12.793-29.86l-0.007-0.007zM853.333 768h-682.667v-91.733l66.133-68.267c24.782-24.511 40.224-58.423 40.533-95.941l0-0.059v-149.333c0-113.067 61.867-234.667 234.667-234.667s234.667 125.867 234.667 234.667v134.4c0.879 47.11 19.381 89.736 49.168 121.71l-0.102-0.11 57.6 57.6z"
/>
</svg>
<div
Expand Down Expand Up @@ -1481,7 +1481,7 @@ exports[`PendingApprovalPage should render 1`] = `
Pending Members Approval
<br />
(
Admin View) *
Admin View)
</span>
</div>
<div
Expand Down
2 changes: 2 additions & 0 deletions ui/src/components/pending-approval/PendingApprovalTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,8 @@ class PendingApprovalTable extends React.Component {
pendingState={this.state.pendingMap[key].pendingState}
view={view}
timeZone={this.props.timeZone}
selfServe={pending.selfServe}
auditRef={pending.auditRef}
/>
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,10 @@ export default class PendingApprovalTableRow extends React.Component {
onChange={(event) => {
this.props.auditRefChange(key, event);
}}
/>
{...(this.props.selfServe && {
defaultValue: this.props.auditRef,
})}
></StyledTextArea>
{this.props.auditRefMissing ? (
<ErrorDiv>Justification is Required</ErrorDiv>
) : null}
Expand Down
27 changes: 27 additions & 0 deletions ui/src/redux/actions/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,30 @@ export const addUsersToStore = (userList) => ({
userList: userList,
},
});

export const STORE_PENDING_ROLE = 'STORE_PENDING_ROLE';
export const storePendingRole = (role, domainName, roleName) => {
console.log('ROLE: ', {
type: STORE_PENDING_ROLE,
payload: {
role,
domainName,
roleName,
},
});

return {
type: STORE_PENDING_ROLE,
payload: {
role,
domainName,
roleName,
},
};
};

export const STORE_PENDING_GROUP = 'STORE_PENDING_GROUP';
export const storePendingGroup = (group, domainName, groupName) => ({
type: STORE_PENDING_GROUP,
payload: { group, domainName, groupName },
});
22 changes: 22 additions & 0 deletions ui/src/redux/reducers/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {
ADD_ALL_USERS,
LOAD_PENDING_MEMBERS,
LOAD_RESOURCE_ACCESS_LIST,
STORE_PENDING_GROUP,
STORE_PENDING_ROLE,
} from '../actions/user';
import {
PROCESS_GROUP_PENDING_MEMBERS_TO_STORE,
Expand Down Expand Up @@ -72,6 +74,26 @@ export const user = (state = {}, action) => {
draft.userList = userList;
});
}
case STORE_PENDING_ROLE: {
return produce(state, (draft) => {
if (!draft.pendingMemberRoles) {
draft.pendingMemberRoles = {};
}
draft.pendingMemberRoles[
`${payload.domainName}:${payload.roleName}`
] = payload.role;
});
}
case STORE_PENDING_GROUP: {
return produce(state, (draft) => {
if (!draft.pendingMemberGroups) {
draft.pendingMemberGroups = {};
}
draft.pendingMemberGroups[
`${payload.domainName}:${payload.groupName}`
] = payload.group;
});
}
default:
return state;
}
Expand Down
6 changes: 4 additions & 2 deletions ui/src/redux/selectors/groups.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ export const selectGroups = (state) => {
};

export const selectGroup = (state, domainName, groupName) => {
return state.groups.groups &&
return state.groups &&
state.groups.groups &&
state.groups.groups[getFullName(domainName, groupDelimiter, groupName)]
? state.groups.groups[
getFullName(domainName, groupDelimiter, groupName)
Expand All @@ -43,7 +44,8 @@ export const thunkSelectGroupMembers = (state, domainName, groupName) => {
};

export const selectGroupMembers = (state, domainName, groupName) => {
return state.groups.groups &&
return state.groups &&
state.groups.groups &&
state.groups.groups[getFullName(domainName, groupDelimiter, groupName)]
? membersMapsToList(
state.groups.groups[
Expand Down
6 changes: 4 additions & 2 deletions ui/src/redux/selectors/roles.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,16 @@ export const selectRoleUsers = (state) => {
};

export const selectRole = (state, domainName, roleName) => {
return state.roles.roles &&
return state.roles &&
state.roles.roles &&
state.roles.roles[getFullName(domainName, roleDelimiter, roleName)]
? state.roles.roles[getFullName(domainName, roleDelimiter, roleName)]
: {};
};

export const selectRoleMembers = (state, domainName, roleName) => {
return state.roles.roles &&
return state.roles &&
state.roles.roles &&
state.roles.roles[getFullName(domainName, roleDelimiter, roleName)] &&
state.roles.roles[getFullName(domainName, roleDelimiter, roleName)]
.roleMembers
Expand Down
5 changes: 5 additions & 0 deletions ui/src/redux/selectors/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,8 @@ export const selectUserResourceAccessList = (state) =>
export const selectAllUsers = (state) => {
return state.user.userList;
};

export const selectPendingMemberRole = (state, domainName, roleName) =>
state?.user?.pendingMemberRoles?.[`${domainName}:${roleName}`] ?? null;
export const selectPendingMemberGroup = (state, domainName, groupName) =>
state?.user?.pendingMemberGroups?.[`${domainName}:${groupName}`] ?? null;
112 changes: 112 additions & 0 deletions ui/src/redux/thunks/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import { getExpiryTime, isExpired } from '../utils';
import {
selectAllUsers,
selectPendingMemberGroup,
selectPendingMemberRole,
selectUserPendingMembers,
selectUserResourceAccessList,
} from '../selectors/user';
Expand All @@ -26,6 +28,8 @@ import {
loadUserPendingMembers,
loadUserResourceAccessList,
returnUserResourceAccessList,
storePendingGroup,
storePendingRole,
} from '../actions/user';

import {
Expand All @@ -34,6 +38,93 @@ import {
loadingSuccess,
} from '../actions/loading';

const ROLE = 'role';
const GROUP = 'group';

const getPendingMemberRole = async (dispatch, state, domainName, roleName) => {
let role = selectPendingMemberRole(state, domainName, roleName);
if (!role || isExpired(role.expiry)) {
role = await API().getRole(domainName, roleName, false, false, true);
role.expiry = getExpiryTime();
dispatch(storePendingRole(role, domainName, roleName));
}
return role;
};
const getPendingMemberGroup = async (
dispatch,
state,
domainName,
groupName
) => {
let group = selectPendingMemberGroup(state, domainName, groupName);
if (!group || isExpired(group.expiry)) {
group = await API().getGroup(domainName, groupName, false, true);
group.expiry = getExpiryTime();
dispatch(storePendingGroup(group, domainName, groupName));
}
return group;
};

const prepareSelfServePendingMembers = async (
pendingMembers,
category,
dispatch,
state
) => {
// SET MEMBER COMMENT AS AUDITREF FOR SELF-SERVE ROLES/GROUPS

// set of domain:role/group to search for
let roleOrGroupSet = new Set();
Object.keys(pendingMembers).forEach((member) => {
const memberData = pendingMembers[member];
if (category === memberData.category) {
roleOrGroupSet.add(
`${memberData.domainName}:${memberData.roleName}`
);
}
});

// setup promises to get roles/groups
let promises = [];
roleOrGroupSet.forEach((entity) => {
let [domain, role] = entity.split(':');
if (category === ROLE) {
promises.push(getPendingMemberRole(dispatch, state, domain, role));
} else if (category === GROUP) {
promises.push(getPendingMemberGroup(dispatch, state, domain, role));
}
});

let data = await Promise.all(promises);

// find which roles/groups have selfServe and assign comment as auditRef
Object.keys(pendingMembers).forEach((memberName) => {
const member = pendingMembers[memberName];
if (member.category !== category) {
return; // category is different (can be role or group)
}
// category matches
for (let i = 0; i < data.length; i++) {
const roleGroup = data[i];
if (!roleGroup.selfServe) {
continue; // we only look for self-serve roles/groups
}
const [domain, rest] = roleGroup.name.split(':');
if (domain !== member.domainName) {
break; // go to next "i" list containing roles/groups for diff domain
}
// domain matches
const [princType, roleGroupName] = rest.split('.');
if (roleGroupName === member.roleName) {
// role/group name matches pending member's role/group and is self serve
// set audetRef as provided comment
member.auditRef = member.userComment;
member.selfServe = true;
}
}
});
};

export const getUserPendingMembers = () => async (dispatch, getState) => {
const expiry = getExpiryTime();
try {
Expand All @@ -44,10 +135,31 @@ export const getUserPendingMembers = () => async (dispatch, getState) => {
) {
let userPendingMembersList =
await API().getPendingDomainMembersList();

let promises = [];
promises.push(
prepareSelfServePendingMembers(
userPendingMembersList,
ROLE,
dispatch,
getState()
)
);
promises.push(
prepareSelfServePendingMembers(
userPendingMembersList,
GROUP,
dispatch,
getState()
)
);
await Promise.all(promises);

dispatch(loadUserPendingMembers(userPendingMembersList, expiry));
}
} catch (error) {
// if error, set userPendingMembers to empty array
console.error(error);
dispatch(loadUserPendingMembers([], expiry));
}
};
Expand Down

0 comments on commit 08a1d28

Please sign in to comment.