From fe08218ae8d999dd10a2d97a4175c203b2efca3d Mon Sep 17 00:00:00 2001 From: Zoltan Runyo <zoltan.runyo@ess.eu> Date: Wed, 20 Oct 2021 17:59:11 +0200 Subject: [PATCH] ICSHWI-8102: Start/stop command notifications --- src/api/SwaggerApi.js | 9 +++ src/components/IOC/IOCService.js | 31 +++++---- .../common/notification/Notifications.js | 63 ++++++++++++++++--- 3 files changed, 80 insertions(+), 23 deletions(-) diff --git a/src/api/SwaggerApi.js b/src/api/SwaggerApi.js index 384030e8..4b023602 100644 --- a/src/api/SwaggerApi.js +++ b/src/api/SwaggerApi.js @@ -263,6 +263,15 @@ export function useDeploymentById() { return useAsync({ fcn: method, call: false, init: null }); } +export function useCommandById() { + const api = useContext(apiContext); + const method = callAndUnpack( + (commandId) => api.apis.Deployments.fetchCommand({ commandId: commandId }), + unpackCommand + ); + return useAsync({ fcn: method, call: false, init: null }); +} + export function unpackDeploymentJob(job) { return { ...job }; } diff --git a/src/components/IOC/IOCService.js b/src/components/IOC/IOCService.js index 0a777368..cd9ea948 100644 --- a/src/components/IOC/IOCService.js +++ b/src/components/IOC/IOCService.js @@ -1,14 +1,15 @@ import { Button, LinearProgress, Grid, Typography } from "@material-ui/core"; -import React, { useState, useEffect, useRef } from "react"; +import React, { useState, useEffect, useRef, useContext } from "react"; import { useStartIOC, useStopIOC } from "../../api/SwaggerApi"; import { CommandJobStatus } from "./CommandJobStatus"; import { SimpleModal } from "../../components/common/SimpleModal/SimpleModal"; import { ConfirmAdHocDialog } from "./ConfirmAdHocDialog"; +import { notificationContext } from "../../components/common/notification/Notifications"; export function IOCService({ ioc, getIOC, ongoingCommand, getCommands }) { const [startJob, startIOC] = useStartIOC(ioc.id); const [stopJob, stopIOC] = useStopIOC(ioc.id); - const [awxCommandId, setAwxCommandId] = useState(null); + const [command, setCommand] = useState(null); const [inProgress, setInProgress] = useState(false); const [action, setAction] = useState(false); const [adHocDialogOpen, setAdHocDialogOpen] = useState(false); @@ -16,6 +17,8 @@ export function IOCService({ ioc, getIOC, ongoingCommand, getCommands }) { const [adHocDialogDescription, setAdHocDialogDescription] = useState(); const callbackRef = useRef(); + const {watchCommand} = useContext(notificationContext); + useEffect(() => { if (ongoingCommand) { if (ongoingCommand.type === "START") { @@ -24,27 +27,29 @@ export function IOCService({ ioc, getIOC, ongoingCommand, getCommands }) { if (ongoingCommand.type === "STOP") { setAction("Stop"); } - setAwxCommandId(ongoingCommand.awxCommandId); + setCommand(ongoingCommand); setInProgress(true); } }, [ongoingCommand]); useEffect(() => { if (startJob) { - setAwxCommandId(startJob.awxCommandId); + watchCommand(startJob.id); + setCommand(startJob); setInProgress(false); } else if (stopJob) { - setAwxCommandId(stopJob.awxCommandId); + watchCommand(stopJob.id); + setCommand(stopJob); setInProgress(false); } - }, [startJob, stopJob]); + }, [startJob, stopJob, watchCommand]); useEffect(() => { const interval = setInterval(() => { - if (action && ((action === "Start" && !ioc.active) || (action === "Stop" && ioc.active))) { + //if (action && ((action === "Start" && !ioc.active) || (action === "Stop" && ioc.active))) { getIOC(); - } - }, 5000); + //} + }, 8000); return () => clearInterval(interval); }, [action, ioc, getIOC]); @@ -60,13 +65,13 @@ export function IOCService({ ioc, getIOC, ongoingCommand, getCommands }) { const start = async () => { setAction("Start"); setInProgress(true); - setAwxCommandId(null); + setCommand(null); await startIOC(); } const stop = async () => { setAction("Stop"); setInProgress(true); - setAwxCommandId(null); + setCommand(null); await stopIOC(); } @@ -106,8 +111,8 @@ export function IOCService({ ioc, getIOC, ongoingCommand, getCommands }) { <Button style={{ backgroundColor: '#4caf50', color: '#FFFFFF' }} onClick={openStartModal}>Start</Button> </Grid> <Grid item xs={12} md={12}> - {awxCommandId ? - <CommandJobStatus awxCommandId={awxCommandId} actionType={action} /> : inProgress ? <LinearProgress color="primary" /> : <></> + {command ? + <CommandJobStatus awxCommandId={command.awxCommandId} actionType={action} /> : inProgress ? <LinearProgress color="primary" /> : <></> } </Grid> </Grid> diff --git a/src/components/common/notification/Notifications.js b/src/components/common/notification/Notifications.js index 9754b299..b94e4b0c 100644 --- a/src/components/common/notification/Notifications.js +++ b/src/components/common/notification/Notifications.js @@ -1,30 +1,38 @@ import React from "react"; import { createContext, useEffect } from "react"; -import { useDeploymentById } from '../../../api/SwaggerApi'; +import { useDeploymentById, useCommandById } from '../../../api/SwaggerApi'; export const notificationContext = createContext({ deploymentNotifications: [], watchDeployment: ()=>{}, clearAll: ()=>{} }); export const WATCHED_DEPLOYMENT_IDS = "WATCHED_DEPLOYMENT_IDS"; export const DEPLOYMENT_NOTIFICATIONS = "DEPLOYMENT_NOTIFICATIONS"; +export const WATCHED_COMMAND_IDS = "WATCHED_COMMAND_IDS"; const DEPLOYMENT_STATUS_POLL_INTERVAL = 3000; +const COMMAND_STATUS_POLL_INTERVAL = 1000; export function NotificationProvider({ children }) { const [deployment, getDeployment] = useDeploymentById(); + const [command, getCommand] = useCommandById(); const watchDeployment = (watchedDeploymentId) => { var watchedIds = JSON.parse(localStorage.getItem(WATCHED_DEPLOYMENT_IDS)); localStorage.setItem(WATCHED_DEPLOYMENT_IDS, JSON.stringify(watchedIds ? [watchedDeploymentId].concat(watchedIds) : [watchedDeploymentId])); } - function removeDeploymentId(id) { - var watchedIds = JSON.parse(localStorage.getItem(WATCHED_DEPLOYMENT_IDS)); + const watchCommand = (watchedCommandId) => { + var watchedIds = JSON.parse(localStorage.getItem(WATCHED_COMMAND_IDS)); + localStorage.setItem(WATCHED_COMMAND_IDS, JSON.stringify(watchedIds ? [watchedCommandId].concat(watchedIds) : [watchedCommandId])); + } + + function removeId(id, storageKey) { + var watchedIds = JSON.parse(localStorage.getItem(storageKey)); if (watchedIds) { var idsAfterRemove = watchedIds.filter(function(item){ return item !== id; }); if (idsAfterRemove.length === 0) { - localStorage.removeItem(WATCHED_DEPLOYMENT_IDS); + localStorage.removeItem(storageKey); } else { - localStorage.setItem(WATCHED_DEPLOYMENT_IDS, JSON.stringify(idsAfterRemove)); + localStorage.setItem(storageKey, JSON.stringify(idsAfterRemove)); } } } @@ -45,10 +53,14 @@ export function NotificationProvider({ children }) { localStorage.setItem(DEPLOYMENT_NOTIFICATIONS, JSON.stringify(notifications)); } + function getStoredIds(storageKey) { + var ids = JSON.parse(localStorage.getItem(storageKey)); + return ids ? ids.map(Number) : []; + } + useEffect(() => { - function getDeploymentIds() { - var ids = JSON.parse(localStorage.getItem(WATCHED_DEPLOYMENT_IDS)); - return ids ? ids.map(Number) : []; + function removeDeploymentId(id) { + removeId(id, WATCHED_DEPLOYMENT_IDS); } function appendNotifications(notification) { @@ -58,7 +70,7 @@ export function NotificationProvider({ children }) { } const interval = setInterval(() => { - const deploymentIds = getDeploymentIds(); + const deploymentIds = getStoredIds(WATCHED_DEPLOYMENT_IDS); for (const deploymentId of deploymentIds) { getDeployment(deploymentId); if(deployment?.id === deploymentId && deployment?.status === "QUEUED") { @@ -79,8 +91,39 @@ export function NotificationProvider({ children }) { return () => clearInterval(interval); }, [deployment, getDeployment]); + useEffect(() => { + function removeCommandId(id) { + removeId(id, WATCHED_COMMAND_IDS); + } + + function appendNotifications(notification) { + if (!deploymentNotifications().includes(notification)) { + setDeploymentNotifications([notification].concat(deploymentNotifications())); + } + } + + const interval = setInterval(() => { + const commandIds = getStoredIds(WATCHED_COMMAND_IDS); + for (const commandId of commandIds) { + getCommand(commandId); + if(command?.id === commandId && command?.status === "PENDING") { + appendNotifications(command.type + " command (" + command.id + ") for " + command.iocName + " is pending"); + } else if(command?.id === commandId && command?.status === "RUNNING") { + appendNotifications(command.type + " command (" + command.id + ") for " + command.iocName + " is running"); + } else if(command?.id === commandId && command?.status === "FAILED") { + appendNotifications(command.type + " command (" + command.id + ") for " + command.iocName + " is failed"); + removeCommandId(command.id); + } else if(command?.id === commandId && command?.status === "SUCCESSFUL") { + appendNotifications(command.type + " command (" + command.id + ") for " + command.iocName + " is successful"); + removeCommandId(command.id); + } + } + }, COMMAND_STATUS_POLL_INTERVAL); + return () => clearInterval(interval); + }, [command, getCommand]); + return ( - <notificationContext.Provider value={{ deploymentNotifications, watchDeployment, clearAll }}> + <notificationContext.Provider value={{ deploymentNotifications, watchDeployment, watchCommand, clearAll }}> {children} </notificationContext.Provider> ); -- GitLab