fix: secret approval loading failed for commiter on approval

This commit is contained in:
Akhil Mohan
2023-12-15 18:10:54 +05:30
parent 99c8dda4e1
commit 80e50d13ec
4 changed files with 70 additions and 60 deletions
@@ -17,12 +17,12 @@ export const getSecretApprovalRequestCount = async (req: Request, res: Response)
} = await validateRequest(reqValidator.getSecretApprovalRequestCount, req); } = await validateRequest(reqValidator.getSecretApprovalRequestCount, req);
if (!(req.authData.authPayload instanceof User)) return; if (!(req.authData.authPayload instanceof User)) return;
const membership = await Membership.findOne({ const membership = await Membership.findOne({
user: req.authData.authPayload._id, user: req.authData.authPayload._id,
workspace: new Types.ObjectId(workspaceId) workspace: new Types.ObjectId(workspaceId)
}); });
if (!membership) throw UnauthorizedRequestError(); if (!membership) throw UnauthorizedRequestError();
const approvalRequestCount = await SecretApprovalRequest.aggregate([ const approvalRequestCount = await SecretApprovalRequest.aggregate([
@@ -73,12 +73,12 @@ export const getSecretApprovalRequests = async (req: Request, res: Response) =>
} = await validateRequest(reqValidator.getSecretApprovalRequests, req); } = await validateRequest(reqValidator.getSecretApprovalRequests, req);
if (!(req.authData.authPayload instanceof User)) return; if (!(req.authData.authPayload instanceof User)) return;
const membership = await Membership.findOne({ const membership = await Membership.findOne({
user: req.authData.authPayload._id, user: req.authData.authPayload._id,
workspace: new Types.ObjectId(workspaceId) workspace: new Types.ObjectId(workspaceId)
}); });
if (!membership) throw UnauthorizedRequestError(); if (!membership) throw UnauthorizedRequestError();
const query = { const query = {
@@ -168,13 +168,13 @@ export const getSecretApprovalRequestDetails = async (req: Request, res: Respons
user: req.authData.authPayload._id, user: req.authData.authPayload._id,
workspace: secretApprovalRequest.workspace workspace: secretApprovalRequest.workspace
}); });
if (!membership) throw UnauthorizedRequestError(); if (!membership) throw UnauthorizedRequestError();
// allow to fetch only if its admin or is the committer or approver // allow to fetch only if its admin or is the committer or approver
if ( if (
membership.role !== "admin" && membership.role !== "admin" &&
secretApprovalRequest.committer !== membership.id && !secretApprovalRequest.committer.equals(membership.id) &&
!secretApprovalRequest.policy.approvers.find( !secretApprovalRequest.policy.approvers.find(
(approverId) => approverId.toString() === membership._id.toString() (approverId) => approverId.toString() === membership._id.toString()
) )
@@ -215,7 +215,7 @@ export const updateSecretApprovalReviewStatus = async (req: Request, res: Respon
user: req.authData.authPayload._id, user: req.authData.authPayload._id,
workspace: secretApprovalRequest.workspace workspace: secretApprovalRequest.workspace
}); });
if (!membership) throw UnauthorizedRequestError(); if (!membership) throw UnauthorizedRequestError();
if ( if (
@@ -257,7 +257,7 @@ export const mergeSecretApprovalRequest = async (req: Request, res: Response) =>
user: req.authData.authPayload._id, user: req.authData.authPayload._id,
workspace: secretApprovalRequest.workspace workspace: secretApprovalRequest.workspace
}); });
if (!membership) throw UnauthorizedRequestError(); if (!membership) throw UnauthorizedRequestError();
if ( if (
@@ -307,7 +307,7 @@ export const updateSecretApprovalRequestStatus = async (req: Request, res: Respo
user: req.authData.authPayload._id, user: req.authData.authPayload._id,
workspace: secretApprovalRequest.workspace workspace: secretApprovalRequest.workspace
}); });
if (!membership) throw UnauthorizedRequestError(); if (!membership) throw UnauthorizedRequestError();
if ( if (
@@ -63,7 +63,7 @@ export const SecretApprovalRequest = () => {
(prev, curr) => ({ ...prev, [curr._id]: curr }), (prev, curr) => ({ ...prev, [curr._id]: curr }),
{} {}
); );
const myMembershipId = members?.find(({ user }) => user._id === presentUser._id)?._id; const myMembershipId = members?.find(({ user }) => user._id === presentUser?._id)?._id;
const isSecretApprovalScreen = Boolean(selectedApproval); const isSecretApprovalScreen = Boolean(selectedApproval);
const handleGoBackSecretRequestDetail = () => { const handleGoBackSecretRequestDetail = () => {
@@ -101,7 +101,7 @@ export const SecretApprovalRequest = () => {
exit={{ opacity: 0, translateX: 30 }} exit={{ opacity: 0, translateX: 30 }}
className="rounded-md text-gray-300" className="rounded-md text-gray-300"
> >
<div className="p-4 px-8 flex items-center space-x-8 bg-mineshaft-800 rounded-t-md border-t border-x border-mineshaft-600"> <div className="flex items-center space-x-8 rounded-t-md border-x border-t border-mineshaft-600 bg-mineshaft-800 p-4 px-8">
<div <div
role="button" role="button"
tabIndex={0} tabIndex={0}
@@ -110,7 +110,7 @@ export const SecretApprovalRequest = () => {
if (evt.key === "Enter") setStatusFilter("open"); if (evt.key === "Enter") setStatusFilter("open");
}} }}
className={ className={
statusFilter === "close" ? "text-gray-500 hover:text-gray-400 duration-100" : "" statusFilter === "close" ? "text-gray-500 duration-100 hover:text-gray-400" : ""
} }
> >
<FontAwesomeIcon icon={faCodeBranch} className="mr-2" /> <FontAwesomeIcon icon={faCodeBranch} className="mr-2" />
@@ -118,7 +118,7 @@ export const SecretApprovalRequest = () => {
</div> </div>
<div <div
className={ className={
statusFilter === "open" ? "text-gray-500 hover:text-gray-400 duration-100" : "" statusFilter === "open" ? "text-gray-500 duration-100 hover:text-gray-400" : ""
} }
role="button" role="button"
tabIndex={0} tabIndex={0}
@@ -130,7 +130,7 @@ export const SecretApprovalRequest = () => {
<FontAwesomeIcon icon={faCheck} className="mr-2" /> <FontAwesomeIcon icon={faCheck} className="mr-2" />
{isSecretApprovalReqCountSuccess && secretApprovalRequestCount.closed} Closed {isSecretApprovalReqCountSuccess && secretApprovalRequestCount.closed} Closed
</div> </div>
<div className="flex-grow flex justify-end space-x-8"> <div className="flex flex-grow justify-end space-x-8">
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger> <DropdownMenuTrigger>
<Button <Button
@@ -185,7 +185,7 @@ export const SecretApprovalRequest = () => {
</DropdownMenu> </DropdownMenu>
</div> </div>
</div> </div>
<div className="flex flex-col border-t border-mineshaft-600 bg-mineshaft-800 rounded-b-md border-b border-x border-mineshaft-600"> <div className="flex flex-col rounded-b-md border-x border-t border-b border-mineshaft-600 border-mineshaft-600 bg-mineshaft-800">
{isRequestListEmpty && ( {isRequestListEmpty && (
<div className="py-12"> <div className="py-12">
<EmptyState title="No more requests pending." /> <EmptyState title="No more requests pending." />
@@ -246,9 +246,9 @@ export const SecretApprovalRequest = () => {
> >
<div className="mb-2 flex items-center"> <div className="mb-2 flex items-center">
<FontAwesomeIcon icon={faCodeBranch} className="mr-2" /> <FontAwesomeIcon icon={faCodeBranch} className="mr-2" />
<Skeleton className="bg-mineshaft-600 w-1/4" /> <Skeleton className="w-1/4 bg-mineshaft-600" />
</div> </div>
<Skeleton className="bg-mineshaft-600 w-1/2" /> <Skeleton className="w-1/2 bg-mineshaft-600" />
</div> </div>
))} ))}
</div> </div>
@@ -22,6 +22,7 @@ type Props = {
isMergable?: boolean; isMergable?: boolean;
status: "close" | "open"; status: "close" | "open";
approvals: number; approvals: number;
canApprove?: boolean;
statusChangeByEmail: string; statusChangeByEmail: string;
workspaceId: string; workspaceId: string;
}; };
@@ -33,7 +34,8 @@ export const SecretApprovalRequestAction = ({
isMergable, isMergable,
approvals, approvals,
statusChangeByEmail, statusChangeByEmail,
workspaceId workspaceId,
canApprove
}: Props) => { }: Props) => {
const { createNotification } = useNotificationContext(); const { createNotification } = useNotificationContext();
const { mutateAsync: performSecretApprovalMerge, isLoading: isMerging } = const { mutateAsync: performSecretApprovalMerge, isLoading: isMerging } =
@@ -83,11 +85,11 @@ export const SecretApprovalRequestAction = ({
if (!hasMerged && status === "open") { if (!hasMerged && status === "open") {
return ( return (
<div className="flex justify-between items-center w-full"> <div className="flex w-full items-center justify-between">
<div className="flex space-x-4 items-start"> <div className="flex items-start space-x-4">
<FontAwesomeIcon <FontAwesomeIcon
icon={isMergable ? faSquareCheck : faSquareXmark} icon={isMergable ? faSquareCheck : faSquareXmark}
className={twMerge("text-2xl pt-1", isMergable ? "text-primary" : "text-red-600")} className={twMerge("pt-1 text-2xl", isMergable ? "text-primary" : "text-red-600")}
/> />
<span className="flex flex-col"> <span className="flex flex-col">
{isMergable ? "Good to merge" : "Review required"} {isMergable ? "Good to merge" : "Review required"}
@@ -98,25 +100,31 @@ export const SecretApprovalRequestAction = ({
</span> </span>
</div> </div>
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<Button {canApprove ? (
onClick={() => handleSecretApprovalStatusChange("close")} <>
isLoading={isStatusChanging} <Button
variant="outline_bg" onClick={() => handleSecretApprovalStatusChange("close")}
colorSchema="secondary" isLoading={isStatusChanging}
leftIcon={<FontAwesomeIcon icon={faClose} />} variant="outline_bg"
> colorSchema="secondary"
Close request leftIcon={<FontAwesomeIcon icon={faClose} />}
</Button> >
<Button Close request
leftIcon={<FontAwesomeIcon icon={faCheck} />} </Button>
isDisabled={!isMergable} <Button
isLoading={isMerging} leftIcon={<FontAwesomeIcon icon={faCheck} />}
onClick={handleSecretApprovalRequestMerge} isDisabled={!isMergable}
colorSchema="primary" isLoading={isMerging}
variant="solid" onClick={handleSecretApprovalRequestMerge}
> colorSchema="primary"
Merge variant="solid"
</Button> >
Merge
</Button>
</>
) : (
<div>Only approvers can merge</div>
)}
</div> </div>
</div> </div>
); );
@@ -124,9 +132,9 @@ export const SecretApprovalRequestAction = ({
if (hasMerged && status === "close") if (hasMerged && status === "close")
return ( return (
<div className="flex justify-between items-center w-full"> <div className="flex w-full items-center justify-between">
<div className="flex space-x-4 items-start"> <div className="flex items-start space-x-4">
<FontAwesomeIcon icon={faCheck} className="text-2xl text-primary pt-1" /> <FontAwesomeIcon icon={faCheck} className="pt-1 text-2xl text-primary" />
<span className="flex flex-col"> <span className="flex flex-col">
Change request merged Change request merged
<span className="inline-block text-xs text-bunker-200"> <span className="inline-block text-xs text-bunker-200">
@@ -138,9 +146,9 @@ export const SecretApprovalRequestAction = ({
); );
return ( return (
<div className="flex justify-between items-center w-full"> <div className="flex w-full items-center justify-between">
<div className="flex space-x-4 items-start"> <div className="flex items-start space-x-4">
<FontAwesomeIcon icon={faUserLock} className="text-2xl text-primary pt-1" /> <FontAwesomeIcon icon={faUserLock} className="pt-1 text-2xl text-primary" />
<span className="flex flex-col"> <span className="flex flex-col">
Change request has been closed Change request has been closed
<span className="inline-block text-xs text-bunker-200"> <span className="inline-block text-xs text-bunker-200">
@@ -108,6 +108,7 @@ export const SecretApprovalRequestChanges = ({
({ user: membershipUser }) => membershipUser.email === user.email ({ user: membershipUser }) => membershipUser.email === user.email
); );
const myMembershipId = myMembership?._id || ""; const myMembershipId = myMembership?._id || "";
const canApprove = secretApprovalRequestDetails?.policy?.approvers?.includes(myMembershipId);
const reviewedMembers = secretApprovalRequestDetails?.reviewers?.reduce< const reviewedMembers = secretApprovalRequestDetails?.reviewers?.reduce<
Record<string, ApprovalStatus> Record<string, ApprovalStatus>
>( >(
@@ -164,30 +165,30 @@ export const SecretApprovalRequestChanges = ({
return ( return (
<div className="flex space-x-6"> <div className="flex space-x-6">
<div className="flex-grow"> <div className="flex-grow">
<div className="flex items-center space-x-4 pt-2 pb-6 sticky top-0 z-20 bg-bunker-800"> <div className="sticky top-0 z-20 flex items-center space-x-4 bg-bunker-800 pt-2 pb-6">
<IconButton variant="outline_bg" ariaLabel="go-back" onClick={onGoBack}> <IconButton variant="outline_bg" ariaLabel="go-back" onClick={onGoBack}>
<FontAwesomeIcon icon={faArrowLeft} /> <FontAwesomeIcon icon={faArrowLeft} />
</IconButton> </IconButton>
<div className="bg-red-600 text-white flex items-center space-x-2 px-4 py-2 rounded-3xl"> <div className="flex items-center space-x-2 rounded-3xl bg-red-600 px-4 py-2 text-white">
<FontAwesomeIcon icon={faCodeBranch} size="sm" /> <FontAwesomeIcon icon={faCodeBranch} size="sm" />
<span>{secretApprovalRequestDetails.status}</span> <span>{secretApprovalRequestDetails.status}</span>
</div> </div>
<div className="flex flex-col flex-grow"> <div className="flex flex-grow flex-col">
<div className="text-lg mb-1"> <div className="mb-1 text-lg">
{generateCommitText(secretApprovalRequestDetails.commits)} {generateCommitText(secretApprovalRequestDetails.commits)}
</div> </div>
<div className="text-sm text-bunker-300 flex items-center"> <div className="flex items-center text-sm text-bunker-300">
{committer?.user?.firstName} {committer?.user?.firstName}
{committer?.user?.lastName} ({committer?.user?.email}) wants to change{" "} {committer?.user?.lastName} ({committer?.user?.email}) wants to change{" "}
{secretApprovalRequestDetails.commits.length} secret values in {secretApprovalRequestDetails.commits.length} secret values in
<span className="text-primary-300 bg-primary-600/60 px-1 mx-1 rounded"> <span className="mx-1 rounded bg-primary-600/60 px-1 text-primary-300">
{secretApprovalRequestDetails.environment} {secretApprovalRequestDetails.environment}
</span> </span>
<div className="flex items-center border border-mineshaft-500 pl-1 pr-2 rounded w-min"> <div className="flex w-min items-center rounded border border-mineshaft-500 pl-1 pr-2">
<div className="border-r border-mineshaft-500 pr-1"> <div className="border-r border-mineshaft-500 pr-1">
<FontAwesomeIcon icon={faFolder} className="text-primary" size="sm" /> <FontAwesomeIcon icon={faFolder} className="text-primary" size="sm" />
</div> </div>
<div className="text-sm pl-2 pb-0.5">{secretApprovalRequestDetails.secretPath}</div> <div className="pl-2 pb-0.5 text-sm">{secretApprovalRequestDetails.secretPath}</div>
</div> </div>
</div> </div>
</div> </div>
@@ -198,7 +199,7 @@ export const SecretApprovalRequestChanges = ({
leftIcon={hasApproved && <FontAwesomeIcon icon={faCheck} />} leftIcon={hasApproved && <FontAwesomeIcon icon={faCheck} />}
onClick={() => handleSecretApprovalStatusUpdate(ApprovalStatus.APPROVED)} onClick={() => handleSecretApprovalStatusUpdate(ApprovalStatus.APPROVED)}
isLoading={isApproving} isLoading={isApproving}
isDisabled={isApproving || hasApproved} isDisabled={isApproving || hasApproved || !canApprove}
> >
{hasApproved ? "Approved" : "Approve"} {hasApproved ? "Approved" : "Approve"}
</Button> </Button>
@@ -208,7 +209,7 @@ export const SecretApprovalRequestChanges = ({
leftIcon={hasRejected && <FontAwesomeIcon icon={faCheck} />} leftIcon={hasRejected && <FontAwesomeIcon icon={faCheck} />}
onClick={() => handleSecretApprovalStatusUpdate(ApprovalStatus.REJECTED)} onClick={() => handleSecretApprovalStatusUpdate(ApprovalStatus.REJECTED)}
isLoading={isRejecting} isLoading={isRejecting}
isDisabled={isRejecting || hasRejected} isDisabled={isRejecting || hasRejected || !canApprove}
> >
{hasRejected ? "Rejected" : "Reject"} {hasRejected ? "Rejected" : "Reject"}
</Button> </Button>
@@ -230,8 +231,9 @@ export const SecretApprovalRequestChanges = ({
) )
)} )}
</div> </div>
<div className="flex items-center px-5 py-6 rounded-lg space-x-6 bg-mineshaft-800 mt-8"> <div className="mt-8 flex items-center space-x-6 rounded-lg bg-mineshaft-800 px-5 py-6">
<SecretApprovalRequestAction <SecretApprovalRequestAction
canApprove={canApprove}
approvalRequestId={secretApprovalRequestDetails._id} approvalRequestId={secretApprovalRequestDetails._id}
hasMerged={hasMerged} hasMerged={hasMerged}
approvals={secretApprovalRequestDetails.policy.approvals || 0} approvals={secretApprovalRequestDetails.policy.approvals || 0}
@@ -244,7 +246,7 @@ export const SecretApprovalRequestChanges = ({
/> />
</div> </div>
</div> </div>
<div className="w-1/5 pt-4 sticky top-0" style={{ minWidth: "240px" }}> <div className="sticky top-0 w-1/5 pt-4" style={{ minWidth: "240px" }}>
<div className="text-sm text-bunker-300">Reviewers</div> <div className="text-sm text-bunker-300">Reviewers</div>
<div className="mt-2 flex flex-col space-y-2 text-sm"> <div className="mt-2 flex flex-col space-y-2 text-sm">
{secretApprovalRequestDetails?.policy?.approvers.map((requiredApproverId) => { {secretApprovalRequestDetails?.policy?.approvers.map((requiredApproverId) => {
@@ -252,7 +254,7 @@ export const SecretApprovalRequestChanges = ({
const status = reviewedMembers?.[requiredApproverId]; const status = reviewedMembers?.[requiredApproverId];
return ( return (
<div <div
className="flex items-center space-x-2 flex-nowrap bg-mineshaft-800 px-2 py-1 rounded" className="flex flex-nowrap items-center space-x-2 rounded bg-mineshaft-800 px-2 py-1"
key={`required-approver-${requiredApproverId}`} key={`required-approver-${requiredApproverId}`}
> >
<div className="flex-grow text-sm"> <div className="flex-grow text-sm">
@@ -278,7 +280,7 @@ export const SecretApprovalRequestChanges = ({
const status = reviewedMembers?.[reviewer.status]; const status = reviewedMembers?.[reviewer.status];
return ( return (
<div <div
className="flex items-center space-x-2 flex-nowrap bg-mineshaft-800 px-2 py-1 rounded" className="flex flex-nowrap items-center space-x-2 rounded bg-mineshaft-800 px-2 py-1"
key={`required-approver-${reviewer.member}`} key={`required-approver-${reviewer.member}`}
> >
<div className="flex-grow text-sm"> <div className="flex-grow text-sm">