diff --git a/app/javascript/react/screens/App/Plan/Plan.js b/app/javascript/react/screens/App/Plan/Plan.js index 4c57fea9b4..23c014220e 100644 --- a/app/javascript/react/screens/App/Plan/Plan.js +++ b/app/javascript/react/screens/App/Plan/Plan.js @@ -14,8 +14,10 @@ class Plan extends React.Component { if (!prevState.planNotStarted && nextProps.planRequestTasks === prevState.planRequestTasks) { return null; } + // todo: compare previousPlanRequestTasks vs. planRequestTasks and send toast if newly errored cancellations return { planRequestTasks: nextProps.planRequestTasks, + previousPlanRequestTasks: prevState.planRequestTasks, planRequestTasksMutable: Immutable.asMutable(nextProps.planRequestTasks), vms: nextProps.vms, vmsMutable: Immutable.asMutable(nextProps.vms), diff --git a/app/javascript/react/screens/App/Plan/PlanActions.js b/app/javascript/react/screens/App/Plan/PlanActions.js index 1d65f9639d..5eba8a4449 100644 --- a/app/javascript/react/screens/App/Plan/PlanActions.js +++ b/app/javascript/react/screens/App/Plan/PlanActions.js @@ -16,6 +16,8 @@ import { CANCEL_V2V_PLAN_REQUEST_TASKS } from './PlanConstants'; +import { getCancellationRequestIds } from './helpers'; + import { V2V_NOTIFICATION_ADD } from '../common/NotificationList/NotificationConstants'; // ***************************************************************************** @@ -102,7 +104,28 @@ const _getTasksForAllRequestsForPlanActionCreator = (url, allRequests) => dispat resources: allRequests }) .then(response => { - resolve(response); + if (response.data && response.data.results && response.data.results.length) { + const { results } = response.data; + const cancellationRequests = getCancellationRequestIds(results); + if (cancellationRequests.length) { + API.post(url, { + action: 'query', + resources: cancellationRequests + }) + .then(cancellationReponse => { + if (cancellationReponse.data && cancellationReponse.data.results) { + resolve({ results, cancellationResults: cancellationReponse.data.results }); + } else { + resolve({ results }); + } + }) + .catch(e => reject(e)); + } else { + resolve({ results }); + } + } else { + resolve({ results: [] }); + } }) .catch(e => reject(e)); }) diff --git a/app/javascript/react/screens/App/Plan/PlanReducer.js b/app/javascript/react/screens/App/Plan/PlanReducer.js index 92a01a16b5..c31391b8fc 100644 --- a/app/javascript/react/screens/App/Plan/PlanReducer.js +++ b/app/javascript/react/screens/App/Plan/PlanReducer.js @@ -50,9 +50,16 @@ const excludeDownloadDoneTaskId = (allDownloadLogInProgressTaskIds, taskId) => const includeDownloadInProgressTaskId = (allDownloadLogInProgressTaskIds, taskId) => allDownloadLogInProgressTaskIds ? allDownloadLogInProgressTaskIds.concat(taskId) : [taskId]; -const processVMTasks = vmTasks => { +const processVMTasks = (vmTasks, cancellationTasks) => { const tasks = []; - vmTasks.forEach(task => { + for (const vmTask of vmTasks) { + const cancellationTask = cancellationTasks.find(t => t.source_id === vmTask.source_id); + + // cancellationTask represents the most recent corresponding cancellation task for this task + const task = cancellationTask || vmTask; + + // todo: use cancellationTask in Plan Details status. If it exists, and task + // is complete, show the "ban" icon instead const taskDetails = { id: task.id, message: V2V_MIGRATION_STATUS_MESSAGES[task.message], @@ -61,12 +68,15 @@ const processVMTasks = vmTasks => { updated_on: new Date(task.updated_on), completed: task.message === STATUS_MESSAGE_KEYS.VM_MIGRATIONS_COMPLETED || - task.message === STATUS_MESSAGE_KEYS.VM_MIGRATIONS_FAILED, + task.message === STATUS_MESSAGE_KEYS.VM_MIGRATIONS_FAILED, // cancellation status? state: task.state, status: task.status, - options: {} + options: {}, + cancellationTask }; + // many attributes below will likely not exist on the cancellationTask, we will have + // to check const hasPlaybookService = task.options.playbooks; if (hasPlaybookService) { @@ -126,13 +136,18 @@ const processVMTasks = vmTasks => { taskDetails.percentComplete = Math.round(percentComplete * 1000) / 10; } tasks.push(taskDetails); - }); + } + return tasks; }; -const allVMTasksForRequestOfPlan = (requestWithTasks, actions) => { +const allVMTasksForRequestOfPlan = (requestWithTasks, cancellationRequestsWithTasks, actions) => { const tasksOfPlan = getMostRecentVMTasksFromRequests(requestWithTasks, actions); - return processVMTasks(tasksOfPlan); + let cancellationTasksOfPlan = []; + if (cancellationRequestsWithTasks) { + cancellationTasksOfPlan = getMostRecentVMTasksFromRequests(cancellationRequestsWithTasks, actions); + } + return processVMTasks(tasksOfPlan, cancellationTasksOfPlan); }; export default (state = initialState, action) => { @@ -156,17 +171,20 @@ export default (state = initialState, action) => { case `${FETCH_V2V_ALL_REQUESTS_WITH_TASKS_FOR_PLAN}_PENDING`: return state.set('isFetchingPlanRequest', true).set('isRejectedPlanRequest', false); case `${FETCH_V2V_ALL_REQUESTS_WITH_TASKS_FOR_PLAN}_FULFILLED`: - if (action.payload.data) { + if (action.payload.results) { return state .set( 'planRequestTasks', - allVMTasksForRequestOfPlan(action.payload.data.results, state.plan.options.config_info.actions) + allVMTasksForRequestOfPlan( + action.payload.results, + action.payload.cancellationResults, + state.plan.options.config_info.actions + ) ) - .set('allRequestsWithTasksForPlan', action.payload.data.results) .set('planRequestPreviouslyFetched', true) .set( 'planRequestFailed', - commonUtilitiesHelper.getMostRecentEntityByCreationDate(action.payload.data.results).status === 'Error' + commonUtilitiesHelper.getMostRecentEntityByCreationDate(action.payload.results).status === 'Error' ) .set('isRejectedPlanRequest', false) .set('errorPlanRequest', null) diff --git a/app/javascript/react/screens/App/Plan/helpers.js b/app/javascript/react/screens/App/Plan/helpers.js new file mode 100644 index 0000000000..6e2b037799 --- /dev/null +++ b/app/javascript/react/screens/App/Plan/helpers.js @@ -0,0 +1,13 @@ +export const getCancellationRequestIds = requestsWithTasks => { + const cancellationRequestIds = []; + for (const request of requestsWithTasks) { + for (const task of request.miq_request_tasks) { + if (task.options.cancellation_request_id) { + cancellationRequestIds.push(task.options.cancellation_request_id); + } + } + } + // to return a mock cancellation task, just return a request that you know exists... + // return [{ href: 'http://0.0.0.0:8080/api/requests/3' }]; + return cancellationRequestIds; +};