Skip to content

Commit

Permalink
Added Notifications for Approvals
Browse files Browse the repository at this point in the history
  • Loading branch information
Lucifergene committed Apr 9, 2024
1 parent 3c7ab20 commit 8963f19
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 1 deletion.
7 changes: 7 additions & 0 deletions frontend/packages/pipelines-plugin/console-extensions.json
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,13 @@
"column": "right"
}
},
{
"type": "console.context-provider",
"properties": {
"provider": { "$codeRef": "pipelineApprovalContext.PipelineApprovalContextProvider" },
"useValueHook": { "$codeRef": "pipelineApprovalContext.usePipelineApprovalToast" }
}
},
{
"type": "console.page/resource/list",
"properties": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,11 @@
"in": "in",
"Reason": "Reason",
"Submit": "Submit",
"Task approval required": "Task approval required",
"Your approval has been requested on {{plrs}} pipeline runs.": "Your approval has been requested on {{plrs}} pipeline runs.",
"Go to Approvals tab": "Go to Approvals tab",
"Your approval has been requested on {{plrs}} pipeline runs in other namespaces.": "Your approval has been requested on {{plrs}} pipeline runs in other namespaces.",
"Go to Admin Approvals tab": "Go to Admin Approvals tab",
"{{taskLabel}} details": "{{taskLabel}} details",
"CustomRun": "CustomRun",
"CustomRuns": "CustomRuns",
Expand Down
3 changes: 2 additions & 1 deletion frontend/packages/pipelines-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"tasksComponent": "src/components/tasks-index.ts",
"triggersComponent": "src/components/triggers-index.ts",
"pacComponent": "src/components/pac/index.ts",
"resultsComponent": "src/components/shared/results"
"resultsComponent": "src/components/shared/results",
"pipelineApprovalContext": "src/components/tasks/approval-tasks/pipeline-approval-context.ts"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import * as React from 'react';
import { AlertVariant } from '@patternfly/react-core';
import { useTranslation } from 'react-i18next';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: FIXME missing exports due to out-of-sync @types/react-redux version
import { useSelector } from 'react-redux';
import { UserInfo, WatchK8sResource, getUser } from '@console/dynamic-plugin-sdk';
import { getGroupVersionKindForModel } from '@console/dynamic-plugin-sdk/src/utils/k8s/k8s-ref';
import { history } from '@console/internal/components/utils';
import { useK8sWatchResource } from '@console/internal/components/utils/k8s-watch-hook';
import { RootState } from '@console/internal/redux';
import { ApprovalTaskModel } from '@console/pipelines-plugin/src/models';
import { ApprovalTaskKind } from '@console/pipelines-plugin/src/types';
import { useToast, useActiveNamespace } from '@console/shared/src';

const getPipelineRunNameofEachApprover = (approvalTask: ApprovalTaskKind): string => {
/* TODO: Use the label tekton.dev/pipelineRunName */
return approvalTask?.metadata?.name?.split('-').slice(0, -1).join('-');
};

const getPipelineRunsofApprovals = (approvalTasks: ApprovalTaskKind[]): string[] => {
const pipelineRuns = [];
approvalTasks.forEach((approvalTask) => {
pipelineRuns.push(getPipelineRunNameofEachApprover(approvalTask));
});
return pipelineRuns;
};

const checkUserIsApprover = (approvalTask: ApprovalTaskKind, user: string): boolean => {
const approverList = approvalTask?.status?.approvals ?? [];
if (user === 'kube:admin' && approverList?.includes('kubernetes-admin')) {
return true;
}
return approverList?.includes(user);
};

export const PipelineApprovalContext = React.createContext({});

export const PipelineApprovalContextProvider = PipelineApprovalContext.Provider;

export const usePipelineApprovalToast = () => {
const { t } = useTranslation();
const toast = useToast();
const [namespace] = useActiveNamespace();
const user: UserInfo = useSelector<RootState, object>(getUser);
const [currentToasts, setCurrentToasts] = React.useState<{ [key: string]: { toastId: string } }>(
{},
);
const devconsolePath = `/dev-pipelines/ns/${namespace}/approvals?rowFilter-status=wait`;
const adminconsolePath = `pipelines/all-namespaces/approvals?rowFilter-status=wait`;

const approvalsResource: WatchK8sResource = {
groupVersionKind: getGroupVersionKindForModel(ApprovalTaskModel),
isList: true,
};
const [approvalTasks] = useK8sWatchResource<ApprovalTaskKind[]>(approvalsResource);

React.useEffect(() => {
if (currentToasts?.current?.toastId) {
toast.removeToast(currentToasts.current.toastId);
setCurrentToasts((toasts) => ({ ...toasts, current: { toastId: '' } }));
}
if (currentToasts?.other?.toastId) {
toast.removeToast(currentToasts.other.toastId);
setCurrentToasts((toasts) => ({ ...toasts, other: { toastId: '' } }));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [approvalTasks, toast]);

React.useEffect(() => {
let toastID = '';
const userApprovalTasks = approvalTasks.filter((approvalTask) =>
checkUserIsApprover(approvalTask, user.username),
);
const [currentNsApprovalTasks, otherNsApprovalTasks]: [
ApprovalTaskKind[],
ApprovalTaskKind[],
] = userApprovalTasks.reduce(
(acc, approvalTask) => {
approvalTask?.metadata?.namespace === namespace
? acc[0].push(approvalTask)
: acc[1].push(approvalTask);
return acc;
},
[[], []],
);

if (currentNsApprovalTasks.length > 0) {
/* TODO: Substract the PipelineRuns which have timed-out */
const uniquePipelineRuns = new Set(getPipelineRunsofApprovals(currentNsApprovalTasks)).size;

if (uniquePipelineRuns > 0) {
toastID = toast.addToast({
variant: AlertVariant.info,
title: t('pipelines-plugin~Task approval required'),
content: t(
'pipelines-plugin~Your approval has been requested on {{plrs}} pipeline runs.',
{
plrs: uniquePipelineRuns,
},
),
dismissible: true,
timeout: 16000,
actions: [
{
dismiss: true,
label: t('pipelines-plugin~Go to Approvals tab'),
callback: () => history.push(devconsolePath),
},
],
});
}
setCurrentToasts((toasts) => ({ ...toasts, current: { toastId: toastID } }));
}

if (otherNsApprovalTasks.length > 0) {
/* TODO: Substract the PipelineRuns which have timed-out */
const uniquePipelineRuns = new Set(getPipelineRunsofApprovals(otherNsApprovalTasks)).size;

if (uniquePipelineRuns > 0) {
toastID = toast.addToast({
variant: AlertVariant.info,
title: t('pipelines-plugin~Task approval required'),
content: t(
'pipelines-plugin~Your approval has been requested on {{plrs}} pipeline runs in other namespaces.',
{
plrs: uniquePipelineRuns,
},
),
dismissible: true,
timeout: 16000,
actions: [
{
dismiss: true,
label: t('pipelines-plugin~Go to Admin Approvals tab'),
callback: () => history.push(adminconsolePath),
},
],
});
}
setCurrentToasts((toasts) => ({ ...toasts, other: { toastId: toastID } }));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [approvalTasks, user, t, toast]);
};

0 comments on commit 8963f19

Please sign in to comment.