From e79794a5d3dbbb3b8b57d55f473d164fef399282 Mon Sep 17 00:00:00 2001 From: Mike Turley Date: Fri, 16 Nov 2018 16:36:30 -0500 Subject: [PATCH 01/11] Handle denied state of a plan request in the in-progress view (with temporary condition) --- .../components/Migrations/InProgressCard.js | 5 ++- .../Migrations/MigrationsInProgressCard.js | 42 ++++++++++++++++++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/app/javascript/react/screens/App/Overview/components/Migrations/InProgressCard.js b/app/javascript/react/screens/App/Overview/components/Migrations/InProgressCard.js index 50926c3f94..c98a93d9f8 100644 --- a/app/javascript/react/screens/App/Overview/components/Migrations/InProgressCard.js +++ b/app/javascript/react/screens/App/Overview/components/Migrations/InProgressCard.js @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Card, Grid, noop } from 'patternfly-react'; -const InProgressCard = ({ title, children, onClick, ...props }) => ( +const InProgressCard = ({ title, children, footer, onClick, ...props }) => ( ( > {title} {children} + {footer} ); @@ -19,12 +20,14 @@ const InProgressCard = ({ title, children, onClick, ...props }) => ( InProgressCard.propTypes = { title: PropTypes.node, children: PropTypes.node, + footer: PropTypes.node, onClick: PropTypes.func }; InProgressCard.defaultProps = { title: '', children: null, + footer: null, onClick: noop }; diff --git a/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCard.js b/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCard.js index e8b958098d..2e27a4ffe5 100644 --- a/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCard.js +++ b/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCard.js @@ -1,7 +1,17 @@ import React from 'react'; import PropTypes from 'prop-types'; import numeral from 'numeral'; -import { EmptyState, Icon, OverlayTrigger, Popover, Tooltip, UtilizationBar, Spinner } from 'patternfly-react'; +import { + EmptyState, + Icon, + OverlayTrigger, + Popover, + Tooltip, + UtilizationBar, + Spinner, + Card, + Button +} from 'patternfly-react'; import InProgressCard from './InProgressCard'; import InProgressWithDetailCard from './InProgressWithDetailCard'; import TickingIsoElapsedTime from '../../../../../../components/dates/TickingIsoElapsedTime'; @@ -36,6 +46,36 @@ const MigrationsInProgressCard = ({ ); } + // TODO: remove plan.name condition here + if (mostRecentRequest.approval_state === 'denied' || plan.name === 'test-denied-state') { + const cardEmptyState = ( + + + + {__('Unable to start migration because no conversion host is configured.')}{' '} + + {__('See the product documentation for information on configuring conversion hosts.')} + + + + ); + const cardFooter = ( + + + + ); + return ( + {plan.name}} footer={cardFooter}> + {cardEmptyState} + + ); + } + // UX business rule: reflect failed immediately if any single task has failed // in the most recent request let failed = false; From 293ad42cb92bb9ff1df6ef323e59d0ee9d2d4855 Mon Sep 17 00:00:00 2001 From: Mike Turley Date: Mon, 19 Nov 2018 17:31:34 -0500 Subject: [PATCH 02/11] Handle denied state in the Plans Completed view --- .../Migrations/MigrationsCompletedList.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsCompletedList.js b/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsCompletedList.js index 395dd47107..502ce54e7f 100644 --- a/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsCompletedList.js +++ b/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsCompletedList.js @@ -85,6 +85,7 @@ const MigrationsCompletedList = ({ requestsOfAssociatedPlan.length > 0 && getMostRecentRequest(requestsOfAssociatedPlan); const failed = mostRecentRequest && mostRecentRequest.status === 'Error'; + const denied = mostRecentRequest && mostRecentRequest.status === 'Denied'; const tasks = {}; let tasksOfPlan = {}; @@ -209,7 +210,7 @@ const MigrationsCompletedList = ({ ) : ( ), - - - {elapsedTime} - , + !denied ? ( + + + {elapsedTime} + + ) : null, migrationScheduled && !staleMigrationSchedule && !migrationStarting ? ( @@ -259,7 +262,7 @@ const MigrationsCompletedList = ({ actions={
{!archived && - failed && ( + (failed || denied) && ( {showInitialScheduleButton && scheduleButtons} diff --git a/app/javascript/react/screens/App/Plan/Plan.js b/app/javascript/react/screens/App/Plan/Plan.js index 7beda5aa65..857b4725df 100644 --- a/app/javascript/react/screens/App/Plan/Plan.js +++ b/app/javascript/react/screens/App/Plan/Plan.js @@ -112,6 +112,7 @@ class Plan extends React.Component { planName, planArchived, planRequestFailed, + planRequestDenied, isRejectedPlanRequest, isFetchingPlanRequest, isRejectedPlan, @@ -155,7 +156,7 @@ class Plan extends React.Component { archived: }; const breadcrumbIcon = - (planRequestFailed && icons.failed) || + ((planRequestFailed || planRequestDenied) && icons.failed) || (planArchived && icons.archived) || (planFinished && icons.success) || (!planNotStarted && icons.inProgress) || @@ -270,6 +271,7 @@ Plan.propTypes = { fetchTasksForAllRequestsForPlanAction: PropTypes.func.isRequired, planName: PropTypes.string, planRequestFailed: PropTypes.bool, + planRequestDenied: PropTypes.bool, planArchived: PropTypes.bool, planRequestTasks: PropTypes.array, isRejectedPlanRequest: PropTypes.bool, diff --git a/app/javascript/react/screens/App/Plan/PlanReducer.js b/app/javascript/react/screens/App/Plan/PlanReducer.js index 793f42e1d3..ec4036a92b 100644 --- a/app/javascript/react/screens/App/Plan/PlanReducer.js +++ b/app/javascript/react/screens/App/Plan/PlanReducer.js @@ -36,6 +36,7 @@ export const initialState = Immutable({ errorPlanRequest: null, planRequestTasks: [], planRequestFailed: false, + planRequestDenied: false, isFetchingPlan: false, isRejectedPlan: false, errorPlan: null, @@ -92,9 +93,8 @@ export default (state = initialState, action) => { state.plan.options.config_info.actions ); - const tasksOfMostRecentRequest = commonUtilitiesHelper.getMostRecentEntityByCreationDate( - action.payload.data.results - ).miq_request_tasks; + const mostRecentRequest = commonUtilitiesHelper.getMostRecentEntityByCreationDate(action.payload.data.results); + const tasksOfMostRecentRequest = mostRecentRequest.miq_request_tasks; const vmsBeingProcessedInCurrentRun = tasksOfMostRecentRequest.map(task => task.source_id); const tasksCompletedSuccessfullyInPriorRuns = vmTasksForRequestOfPlan.filter( task => vmsBeingProcessedInCurrentRun.indexOf(task.source_id) === -1 @@ -120,10 +120,8 @@ export default (state = initialState, action) => { ) .set('allRequestsWithTasksForPlan', action.payload.data.results) .set('planRequestPreviouslyFetched', true) - .set( - 'planRequestFailed', - commonUtilitiesHelper.getMostRecentEntityByCreationDate(action.payload.data.results).status === 'Error' - ) + .set('planRequestFailed', mostRecentRequest.status === 'Error') + .set('planRequestDenied', mostRecentRequest.status === 'Denied') .set('failedMigrations', getFailedMigrations(vmTasksForRequestOfPlan)) .set('successfulMigrations', getSuccessfulMigrations(vmTasksForRequestOfPlan)) .set('isRejectedPlanRequest', false) @@ -162,7 +160,8 @@ export default (state = initialState, action) => { .set('planRequestPreviouslyFetched', false) .set('markedForCancellation', []) .set('selectedTasksForCancel', []) - .set('planRequestFailed', false); + .set('planRequestFailed', false) + .set('planRequestDenied', false); case `${FETCH_V2V_MIGRATION_TASK_LOG}_PENDING`: return state.set('isFetchingMigrationTaskLog', true).set('isRejectedMigrationTaskLog', false); From db8647bea5ee7262d287a14153af73dbee20ef7a Mon Sep 17 00:00:00 2001 From: Mike Turley Date: Mon, 19 Nov 2018 17:50:35 -0500 Subject: [PATCH 05/11] Add error message about conversion hosts to denied Plan request details page --- app/javascript/react/screens/App/Plan/Plan.js | 18 ++++++++++++++++++ .../App/Plan/components/PlanEmptyState.js | 7 ++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/app/javascript/react/screens/App/Plan/Plan.js b/app/javascript/react/screens/App/Plan/Plan.js index 857b4725df..7b38e3025a 100644 --- a/app/javascript/react/screens/App/Plan/Plan.js +++ b/app/javascript/react/screens/App/Plan/Plan.js @@ -243,6 +243,24 @@ class Plan extends React.Component { )} {planNotStarted && !isRejectedVms && vmsMutable.length > 0 && } {planNotStarted && + planRequestDenied && ( + + {__('Unable to start migration because no conversion host is configured.')}{' '} + + {__('See the product documentation for information on configuring conversion hosts.')} + + + } + descriptionIsNode + /> + )} + {planNotStarted && + !planRequestDenied && vmsMutable.length === 0 && ( ( +const PlanEmptyState = ({ title, iconType, iconName, description, descriptionIsNode }) => (

{sprintf(__('%s'), title)} - {sprintf(__('%s'), description)} + {descriptionIsNode ? description : sprintf(__('%s'), description)}
); @@ -17,7 +17,8 @@ PlanEmptyState.propTypes = { title: PropTypes.string, iconType: PropTypes.string, iconName: PropTypes.string, - description: PropTypes.string + description: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), + descriptionIsNode: PropTypes.bool }; export default PlanEmptyState; From 8fb05e652db53a1f6286a4b1b29f20c275f6151e Mon Sep 17 00:00:00 2001 From: Mike Turley Date: Tue, 20 Nov 2018 11:10:07 -0500 Subject: [PATCH 06/11] Open docs link in a new tab --- .../components/Migrations/MigrationsInProgressCard.js | 3 ++- app/javascript/react/screens/App/Plan/Plan.js | 4 ++-- app/javascript/react/screens/App/Plan/PlanConstants.js | 3 +++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCard.js b/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCard.js index 42e9c01905..d3701e12ad 100644 --- a/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCard.js +++ b/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCard.js @@ -19,6 +19,7 @@ import getMostRecentRequest from '../../../common/getMostRecentRequest'; import getMostRecentVMTasksFromRequests from './helpers/getMostRecentVMTasksFromRequests'; import getPlaybookName from './helpers/getPlaybookName'; import { PLAN_JOB_STATES } from '../../../../../../data/models/plans'; +import { DOCS_URL_CONFIGURE_CONVERSION_HOSTS } from '../../../Plan/PlanConstants'; const MigrationsInProgressCard = ({ plan, @@ -53,7 +54,7 @@ const MigrationsInProgressCard = ({ {__('Unable to start migration because no conversion host is configured.')}{' '} - + {__('See the product documentation for information on configuring conversion hosts.')} diff --git a/app/javascript/react/screens/App/Plan/Plan.js b/app/javascript/react/screens/App/Plan/Plan.js index 7b38e3025a..cea92672d5 100644 --- a/app/javascript/react/screens/App/Plan/Plan.js +++ b/app/javascript/react/screens/App/Plan/Plan.js @@ -14,7 +14,7 @@ import { ACTIVE_PLAN_SORT_FIELDS, FINISHED_PLAN_SORT_FIELDS } from './components/PlanRequestDetailList/PlanRequestDetailListConstants'; -import { REQUEST_TASKS_URL } from './PlanConstants'; +import { REQUEST_TASKS_URL, DOCS_URL_CONFIGURE_CONVERSION_HOSTS } from './PlanConstants'; class Plan extends React.Component { static getDerivedStateFromProps(nextProps, prevState) { @@ -251,7 +251,7 @@ class Plan extends React.Component { description={ {__('Unable to start migration because no conversion host is configured.')}{' '} - + {__('See the product documentation for information on configuring conversion hosts.')} diff --git a/app/javascript/react/screens/App/Plan/PlanConstants.js b/app/javascript/react/screens/App/Plan/PlanConstants.js index 7395d63d3c..b3287c7eb0 100644 --- a/app/javascript/react/screens/App/Plan/PlanConstants.js +++ b/app/javascript/react/screens/App/Plan/PlanConstants.js @@ -74,3 +74,6 @@ STATUS_MESSAGES[STATUS_MESSAGE_KEYS.VM_TRANSFORMATIONS_FAILED] = __('VM migratio STATUS_MESSAGES[STATUS_MESSAGE_KEYS.CANCELLED] = __('VM cancelled'); export { STATUS_MESSAGES as V2V_MIGRATION_STATUS_MESSAGES }; + +// TODO FIXME this is a 404: +export const DOCS_URL_CONFIGURE_CONVERSION_HOSTS = "https://access.redhat.com/documentation/en-us/red_hat_infrastructure_migration_solution/1.0/html/infrastructure_migration_solution_guide/installation#rhv_conversion_hosts"; From 4c04f74643383c83fa549c7c2b764be2e703b8ba Mon Sep 17 00:00:00 2001 From: Mike Turley Date: Tue, 20 Nov 2018 16:01:40 -0500 Subject: [PATCH 07/11] Change wording of error to be more accurate with respect to timing --- .../Overview/components/Migrations/MigrationsInProgressCard.js | 2 +- app/javascript/react/screens/App/Plan/Plan.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCard.js b/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCard.js index d3701e12ad..6b986b1984 100644 --- a/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCard.js +++ b/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCard.js @@ -53,7 +53,7 @@ const MigrationsInProgressCard = ({ - {__('Unable to start migration because no conversion host is configured.')}{' '} + {__('Unable to migrate VMs because no conversion host was configured at the time of the attempted migration.')}{' '} {__('See the product documentation for information on configuring conversion hosts.')} diff --git a/app/javascript/react/screens/App/Plan/Plan.js b/app/javascript/react/screens/App/Plan/Plan.js index cea92672d5..3ef58e3131 100644 --- a/app/javascript/react/screens/App/Plan/Plan.js +++ b/app/javascript/react/screens/App/Plan/Plan.js @@ -250,7 +250,7 @@ class Plan extends React.Component { iconName="error-circle-o" description={ - {__('Unable to start migration because no conversion host is configured.')}{' '} + {__('Unable to migrate VMs because no conversion host was configured at the time of the attempted migration.')}{' '} {__('See the product documentation for information on configuring conversion hosts.')} From 70bd78bb6f05d8fb068fe76cd2dc0d5cf6c03a50 Mon Sep 17 00:00:00 2001 From: Mike Turley Date: Tue, 20 Nov 2018 17:34:06 -0500 Subject: [PATCH 08/11] Filter denied requests into views properly, implement acknowledge denial action --- .../react/screens/App/Overview/Overview.js | 12 +++++- .../screens/App/Overview/OverviewActions.js | 38 ++++++++++++++++++- .../screens/App/Overview/OverviewConstants.js | 1 + .../screens/App/Overview/OverviewReducer.js | 24 +++++++++++- .../screens/App/Overview/OverviewSelectors.js | 17 +++++++-- .../components/Migrations/Migrations.js | 17 ++++++++- .../Migrations/MigrationsInProgressCard.js | 32 ++++++++++++---- .../Migrations/MigrationsInProgressCards.js | 19 +++++++++- app/javascript/react/screens/App/Plan/Plan.js | 4 +- .../react/screens/App/Plan/PlanConstants.js | 3 +- 10 files changed, 145 insertions(+), 22 deletions(-) diff --git a/app/javascript/react/screens/App/Overview/Overview.js b/app/javascript/react/screens/App/Overview/Overview.js index d3c28e9db2..a912cece32 100644 --- a/app/javascript/react/screens/App/Overview/Overview.js +++ b/app/javascript/react/screens/App/Overview/Overview.js @@ -270,7 +270,9 @@ class Overview extends React.Component { fetchTransformationMappingsAction, openMappingWizardOnTransitionAction, setMigrationsFilterAction, - initialMigrationsFilterSet + initialMigrationsFilterSet, + acknowledgeDeniedPlanRequestAction, + isEditingPlanRequest } = this.props; const mainContent = ( @@ -311,7 +313,9 @@ class Overview extends React.Component { fetchTransformationPlansAction={fetchTransformationPlansAction} fetchTransformationPlansUrl={fetchTransformationPlansUrl} fetchArchivedTransformationPlansUrl={fetchArchivedTransformationPlansUrl} + isFetchingTransformationPlans={isFetchingTransformationPlans} isFetchingArchivedTransformationPlans={isFetchingArchivedTransformationPlans} + isFetchingAllRequestsWithTasks={isFetchingAllRequestsWithTasks} archiveTransformationPlanAction={archiveTransformationPlanAction} archiveTransformationPlanUrl={archiveTransformationPlanUrl} deleteTransformationPlanAction={deleteTransformationPlanAction} @@ -325,6 +329,8 @@ class Overview extends React.Component { fetchTransformationMappingsUrl={fetchTransformationMappingsUrl} fetchTransformationMappingsAction={fetchTransformationMappingsAction} showEditPlanNameModalAction={showEditPlanNameModalAction} + acknowledgeDeniedPlanRequestAction={acknowledgeDeniedPlanRequestAction} + isEditingPlanRequest={isEditingPlanRequest} /> ) : ( dispatch => }); export const openMappingWizardOnTransitionAction = () => ({ type: OPEN_V2V_MAPPING_WIZARD_ON_MOUNT }); + +const _editPlanRequestActionCreator = ({ planRequestUrl, plansUrl, resource }) => dispatch => + dispatch({ + type: V2V_EDIT_PLAN_REQUEST, + payload: new Promise((resolve, reject) => + API.post(planRequestUrl, { action: 'edit', resource }) + .then(response => { + resolve(response); + fetchTransformationPlansAction({ + url: plansUrl, + archived: false + })(dispatch); + }) + .catch(e => reject(e)) + ) + }); + +export const editPlanRequestAction = ({ planRequestUrl, plansUrl, resource }) => + _editPlanRequestActionCreator({ + planRequestUrl: new URI(planRequestUrl).toString(), + plansUrl: new URI(plansUrl).toString(), + resource + }); + +export const acknowledgeDeniedPlanRequestAction = ({ plansUrl, planRequest }) => + editPlanRequestAction({ + planRequestUrl: planRequest.href, + plansUrl, + resource: { + options: { + ...planRequest.options, + denial_acknowledged: true + } + } + }); diff --git a/app/javascript/react/screens/App/Overview/OverviewConstants.js b/app/javascript/react/screens/App/Overview/OverviewConstants.js index 5d6332d2ec..875112c37b 100644 --- a/app/javascript/react/screens/App/Overview/OverviewConstants.js +++ b/app/javascript/react/screens/App/Overview/OverviewConstants.js @@ -13,6 +13,7 @@ export const V2V_AUTO_SET_MIGRATIONS_FILTER = 'V2V_AUTO_SET_MIGRATIONS_FILTER'; export const V2V_RETRY_MIGRATION = 'V2V_RETRY_MIGRATION'; export const V2V_TOGGLE_SCHEDULE_MIGRATION_MODAL = 'V2V_TOGGLE_SCHEDULE_MIGRATION_MODAL'; export const V2V_SCHEDULE_MIGRATION = 'V2V_SCHEDULE_MIGRATION'; +export const V2V_EDIT_PLAN_REQUEST = 'V2V_EDIT_PLAN_REQUEST'; export const SHOW_CONFIRM_MODAL = 'SHOW_CONFIRM_MODAL'; export const HIDE_CONFIRM_MODAL = 'HIDE_CONFIRM_MODAL'; export const ARCHIVE_TRANSFORMATION_PLAN = 'ARCHIVE_TRANSFORMATION_PLAN'; diff --git a/app/javascript/react/screens/App/Overview/OverviewReducer.js b/app/javascript/react/screens/App/Overview/OverviewReducer.js index 4f3417e174..3abab22fae 100644 --- a/app/javascript/react/screens/App/Overview/OverviewReducer.js +++ b/app/javascript/react/screens/App/Overview/OverviewReducer.js @@ -32,7 +32,8 @@ import { ARCHIVE_TRANSFORMATION_PLAN, V2V_TOGGLE_SCHEDULE_MIGRATION_MODAL, V2V_SCHEDULE_MIGRATION, - SHOW_PLAN_WIZARD_EDIT_MODE + SHOW_PLAN_WIZARD_EDIT_MODE, + V2V_EDIT_PLAN_REQUEST } from './OverviewConstants'; import { planTransmutation, sufficientProviders } from './helpers'; @@ -88,7 +89,10 @@ export const initialState = Immutable({ isFetchingServiceTemplatePlaybooks: false, isRejectedServiceTemplatePlaybooks: false, errorServiceTemplatePlaybooks: null, - initialMigrationsFilterSet: false + initialMigrationsFilterSet: false, + isEditingPlanRequest: false, + isRejectedEditingPlanRequest: false, + errorEditingPlanRequest: null }); export default (state = initialState, action) => { @@ -304,6 +308,22 @@ export default (state = initialState, action) => { case V2V_AUTO_SET_MIGRATIONS_FILTER: return state.set('initialMigrationsFilterSet', true); + case `${V2V_EDIT_PLAN_REQUEST}_PENDING`: + return state + .set('isEditingPlanRequest', true) + .set('isRejectedEditingPlanRequest', false) + .set('errorEditingPlanRequest', null); + case `${V2V_EDIT_PLAN_REQUEST}_FULFILLED`: + return state + .set('isEditingPlanRequest', false) + .set('isRejectedEditingPlanRequest', false) + .set('errorEditingPlanRequest', null); + case `${V2V_EDIT_PLAN_REQUEST}_REJECTED`: + return state + .set('isEditingPlanRequest', false) + .set('isRejectedEditingPlanRequest', true) + .set('errorEditingPlanRequest', action.payload); + default: return state; } diff --git a/app/javascript/react/screens/App/Overview/OverviewSelectors.js b/app/javascript/react/screens/App/Overview/OverviewSelectors.js index a3284da0ba..6375d238a6 100644 --- a/app/javascript/react/screens/App/Overview/OverviewSelectors.js +++ b/app/javascript/react/screens/App/Overview/OverviewSelectors.js @@ -10,7 +10,11 @@ export const activeTransformationPlansFilter = (transformationPlans, planId) => } if (transformationPlan.miq_requests.length > 0) { const mostRecentRequest = getMostRecentRequest(transformationPlan.miq_requests); - return mostRecentRequest.request_state === 'active' || mostRecentRequest.request_state === 'pending'; + return ( + mostRecentRequest.request_state === 'active' || + mostRecentRequest.request_state === 'pending' || + (mostRecentRequest.approval_state === 'denied' && !mostRecentRequest.options.denial_acknowledged) + ); } return false; }); @@ -19,7 +23,11 @@ export const finishedTransformationPlansFilter = transformationPlans => transformationPlans.filter(transformationPlan => { if (transformationPlan.miq_requests.length > 0) { const mostRecentRequest = getMostRecentRequest(transformationPlan.miq_requests); - return mostRecentRequest.request_state === 'finished' || mostRecentRequest.request_state === 'failed'; + return ( + (mostRecentRequest.request_state === 'finished' && mostRecentRequest.approval_state !== 'denied') || + mostRecentRequest.request_state === 'failed' || + (mostRecentRequest.approval_state === 'denied' && mostRecentRequest.options.denial_acknowledged) + ); } return false; }); @@ -28,7 +36,10 @@ export const finishedWithErrorTransformationPlansFilter = transformationPlans => transformationPlans.filter(transformationPlan => { if (transformationPlan.miq_requests.length > 0) { const mostRecentRequest = getMostRecentRequest(transformationPlan.miq_requests); - return mostRecentRequest.request_state === 'finished' && mostRecentRequest.status === 'Error'; + return ( + (mostRecentRequest.request_state === 'finished' && mostRecentRequest.status === 'Error') || + mostRecentRequest.approval_state === 'denied' + ); } return false; }); diff --git a/app/javascript/react/screens/App/Overview/components/Migrations/Migrations.js b/app/javascript/react/screens/App/Overview/components/Migrations/Migrations.js index aa1fbc226a..1e0f2fe6f9 100644 --- a/app/javascript/react/screens/App/Overview/components/Migrations/Migrations.js +++ b/app/javascript/react/screens/App/Overview/components/Migrations/Migrations.js @@ -51,7 +51,9 @@ class Migrations extends React.Component { fetchTransformationPlansAction, fetchTransformationPlansUrl, fetchArchivedTransformationPlansUrl, + isFetchingTransformationPlans, isFetchingArchivedTransformationPlans, + isFetchingAllRequestsWithTasks, archivedTransformationPlans, allArchivedPlanRequestsWithTasks, archiveTransformationPlanAction, @@ -66,7 +68,9 @@ class Migrations extends React.Component { showPlanWizardEditModeAction, fetchTransformationMappingsUrl, fetchTransformationMappingsAction, - showEditPlanNameModalAction + showEditPlanNameModalAction, + acknowledgeDeniedPlanRequestAction, + isEditingPlanRequest } = this.props; const plansExist = transformationPlans.length > 0 || archivedTransformationPlans.length > 0; @@ -137,6 +141,11 @@ class Migrations extends React.Component { reloadCard={reloadCard} loading={isCreatingTransformationPlanRequest !== null} redirectTo={redirectTo} + fetchTransformationPlansUrl={fetchTransformationPlansUrl} + acknowledgeDeniedPlanRequestAction={acknowledgeDeniedPlanRequestAction} + isEditingPlanRequest={isEditingPlanRequest} + isFetchingTransformationPlans={isFetchingTransformationPlans} + isFetchingAllRequestsWithTasks={isFetchingAllRequestsWithTasks} /> )} {activeFilter === MIGRATIONS_FILTERS.completed && ( @@ -214,7 +223,9 @@ Migrations.propTypes = { fetchArchivedTransformationPlansUrl: PropTypes.string, archivedTransformationPlans: PropTypes.array, allArchivedPlanRequestsWithTasks: PropTypes.array, + isFetchingTransformationPlans: PropTypes.bool, isFetchingArchivedTransformationPlans: PropTypes.string, + isFetchingAllRequestsWithTasks: PropTypes.bool, archiveTransformationPlanAction: PropTypes.func, archiveTransformationPlanUrl: PropTypes.string, deleteTransformationPlanAction: PropTypes.func, @@ -227,7 +238,9 @@ Migrations.propTypes = { showPlanWizardEditModeAction: PropTypes.func, fetchTransformationMappingsAction: PropTypes.func, fetchTransformationMappingsUrl: PropTypes.string, - showEditPlanNameModalAction: PropTypes.func + showEditPlanNameModalAction: PropTypes.func, + acknowledgeDeniedPlanRequestAction: PropTypes.func, + isEditingPlanRequest: PropTypes.bool }; Migrations.defaultProps = { transformationPlans: [], diff --git a/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCard.js b/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCard.js index 6b986b1984..9a978b80a0 100644 --- a/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCard.js +++ b/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCard.js @@ -26,7 +26,12 @@ const MigrationsInProgressCard = ({ serviceTemplatePlaybooks, allRequestsWithTasks, reloadCard, - handleClick + handleClick, + fetchTransformationPlansUrl, + acknowledgeDeniedPlanRequestAction, + isEditingPlanRequest, + isFetchingTransformationPlans, + isFetchingAllRequestsWithTasks }) => { const requestsOfAssociatedPlan = allRequestsWithTasks.filter(request => request.source_id === plan.id); const mostRecentRequest = requestsOfAssociatedPlan.length > 0 && getMostRecentRequest(requestsOfAssociatedPlan); @@ -47,14 +52,13 @@ const MigrationsInProgressCard = ({ ); } - // TODO: remove plan.name condition here - if (mostRecentRequest.approval_state === 'denied' || plan.name === 'test-denied-state') { + if (mostRecentRequest.approval_state === 'denied') { const cardEmptyState = ( - {__('Unable to migrate VMs because no conversion host was configured at the time of the attempted migration.')}{' '} - + {__('Unable to migrate VMs because no conversion host was configured at the time of the attempted migration.') /* prettier-ignore */}{' '} + {__('See the product documentation for information on configuring conversion hosts.')} @@ -62,7 +66,16 @@ const MigrationsInProgressCard = ({ ); const cardFooter = ( - @@ -281,7 +294,12 @@ MigrationsInProgressCard.propTypes = { serviceTemplatePlaybooks: PropTypes.array, allRequestsWithTasks: PropTypes.array, reloadCard: PropTypes.bool, - handleClick: PropTypes.func + handleClick: PropTypes.func, + fetchTransformationPlansUrl: PropTypes.string, + acknowledgeDeniedPlanRequestAction: PropTypes.func, + isEditingPlanRequest: PropTypes.bool, + isFetchingTransformationPlans: PropTypes.bool, + isFetchingAllRequestsWithTasks: PropTypes.bool }; export default MigrationsInProgressCard; diff --git a/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCards.js b/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCards.js index b48eab04a7..92f912d5d1 100644 --- a/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCards.js +++ b/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCards.js @@ -10,7 +10,12 @@ const MigrationsInProgressCards = ({ allRequestsWithTasks, reloadCard, loading, - redirectTo + redirectTo, + fetchTransformationPlansUrl, + acknowledgeDeniedPlanRequestAction, + isEditingPlanRequest, + isFetchingTransformationPlans, + isFetchingAllRequestsWithTasks }) => (
@@ -29,6 +34,11 @@ const MigrationsInProgressCards = ({ reloadCard={reloadCard} key={plan.id} handleClick={redirectTo} + fetchTransformationPlansUrl={fetchTransformationPlansUrl} + acknowledgeDeniedPlanRequestAction={acknowledgeDeniedPlanRequestAction} + isEditingPlanRequest={isEditingPlanRequest} + isFetchingTransformationPlans={isFetchingTransformationPlans} + isFetchingAllRequestsWithTasks={isFetchingAllRequestsWithTasks} /> )) ) : ( @@ -52,7 +62,12 @@ MigrationsInProgressCards.propTypes = { allRequestsWithTasks: PropTypes.array, reloadCard: PropTypes.bool, loading: PropTypes.bool, - redirectTo: PropTypes.func + redirectTo: PropTypes.func, + fetchTransformationPlansUrl: PropTypes.string, + acknowledgeDeniedPlanRequestAction: PropTypes.func, + isEditingPlanRequest: PropTypes.bool, + isFetchingTransformationPlans: PropTypes.bool, + isFetchingAllRequestsWithTasks: PropTypes.bool }; MigrationsInProgressCards.defaultProps = { diff --git a/app/javascript/react/screens/App/Plan/Plan.js b/app/javascript/react/screens/App/Plan/Plan.js index 3ef58e3131..723e6e9947 100644 --- a/app/javascript/react/screens/App/Plan/Plan.js +++ b/app/javascript/react/screens/App/Plan/Plan.js @@ -250,8 +250,8 @@ class Plan extends React.Component { iconName="error-circle-o" description={ - {__('Unable to migrate VMs because no conversion host was configured at the time of the attempted migration.')}{' '} - + {__('Unable to migrate VMs because no conversion host was configured at the time of the attempted migration.') /* prettier-ignore */}{' '} + {__('See the product documentation for information on configuring conversion hosts.')} diff --git a/app/javascript/react/screens/App/Plan/PlanConstants.js b/app/javascript/react/screens/App/Plan/PlanConstants.js index b3287c7eb0..00d2c533dc 100644 --- a/app/javascript/react/screens/App/Plan/PlanConstants.js +++ b/app/javascript/react/screens/App/Plan/PlanConstants.js @@ -76,4 +76,5 @@ STATUS_MESSAGES[STATUS_MESSAGE_KEYS.CANCELLED] = __('VM cancelled'); export { STATUS_MESSAGES as V2V_MIGRATION_STATUS_MESSAGES }; // TODO FIXME this is a 404: -export const DOCS_URL_CONFIGURE_CONVERSION_HOSTS = "https://access.redhat.com/documentation/en-us/red_hat_infrastructure_migration_solution/1.0/html/infrastructure_migration_solution_guide/installation#rhv_conversion_hosts"; +export const DOCS_URL_CONFIGURE_CONVERSION_HOSTS = + 'https://access.redhat.com/documentation/en-us/red_hat_infrastructure_migration_solution/1.0/html/infrastructure_migration_solution_guide/installation#rhv_conversion_hosts'; From 651198204ff8c72319785074ccb2301e3074d0cc Mon Sep 17 00:00:00 2001 From: Mike Turley Date: Tue, 20 Nov 2018 18:14:10 -0500 Subject: [PATCH 09/11] Move to Completed Plans view after acknowledging denial --- .../App/Overview/components/Migrations/Migrations.js | 4 +++- .../components/Migrations/MigrationsInProgressCard.js | 9 ++++++--- .../components/Migrations/MigrationsInProgressCards.js | 7 +++++-- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/app/javascript/react/screens/App/Overview/components/Migrations/Migrations.js b/app/javascript/react/screens/App/Overview/components/Migrations/Migrations.js index 1e0f2fe6f9..81d32a99ae 100644 --- a/app/javascript/react/screens/App/Overview/components/Migrations/Migrations.js +++ b/app/javascript/react/screens/App/Overview/components/Migrations/Migrations.js @@ -70,7 +70,8 @@ class Migrations extends React.Component { fetchTransformationMappingsAction, showEditPlanNameModalAction, acknowledgeDeniedPlanRequestAction, - isEditingPlanRequest + isEditingPlanRequest, + setMigrationsFilterAction } = this.props; const plansExist = transformationPlans.length > 0 || archivedTransformationPlans.length > 0; @@ -146,6 +147,7 @@ class Migrations extends React.Component { isEditingPlanRequest={isEditingPlanRequest} isFetchingTransformationPlans={isFetchingTransformationPlans} isFetchingAllRequestsWithTasks={isFetchingAllRequestsWithTasks} + setMigrationsFilterAction={setMigrationsFilterAction} /> )} {activeFilter === MIGRATIONS_FILTERS.completed && ( diff --git a/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCard.js b/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCard.js index 9a978b80a0..ae8db7d79d 100644 --- a/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCard.js +++ b/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCard.js @@ -20,6 +20,7 @@ import getMostRecentVMTasksFromRequests from './helpers/getMostRecentVMTasksFrom import getPlaybookName from './helpers/getPlaybookName'; import { PLAN_JOB_STATES } from '../../../../../../data/models/plans'; import { DOCS_URL_CONFIGURE_CONVERSION_HOSTS } from '../../../Plan/PlanConstants'; +import { MIGRATIONS_FILTERS } from '../../OverviewConstants'; const MigrationsInProgressCard = ({ plan, @@ -31,7 +32,8 @@ const MigrationsInProgressCard = ({ acknowledgeDeniedPlanRequestAction, isEditingPlanRequest, isFetchingTransformationPlans, - isFetchingAllRequestsWithTasks + isFetchingAllRequestsWithTasks, + setMigrationsFilterAction }) => { const requestsOfAssociatedPlan = allRequestsWithTasks.filter(request => request.source_id === plan.id); const mostRecentRequest = requestsOfAssociatedPlan.length > 0 && getMostRecentRequest(requestsOfAssociatedPlan); @@ -72,7 +74,7 @@ const MigrationsInProgressCard = ({ acknowledgeDeniedPlanRequestAction({ plansUrl: fetchTransformationPlansUrl, planRequest: mostRecentRequest - }) + }).then(() => setMigrationsFilterAction(MIGRATIONS_FILTERS.completed)) } disabled={isEditingPlanRequest || isFetchingTransformationPlans || isFetchingAllRequestsWithTasks} > @@ -299,7 +301,8 @@ MigrationsInProgressCard.propTypes = { acknowledgeDeniedPlanRequestAction: PropTypes.func, isEditingPlanRequest: PropTypes.bool, isFetchingTransformationPlans: PropTypes.bool, - isFetchingAllRequestsWithTasks: PropTypes.bool + isFetchingAllRequestsWithTasks: PropTypes.bool, + setMigrationsFilterAction: PropTypes.func }; export default MigrationsInProgressCard; diff --git a/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCards.js b/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCards.js index 92f912d5d1..c200dead57 100644 --- a/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCards.js +++ b/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCards.js @@ -15,7 +15,8 @@ const MigrationsInProgressCards = ({ acknowledgeDeniedPlanRequestAction, isEditingPlanRequest, isFetchingTransformationPlans, - isFetchingAllRequestsWithTasks + isFetchingAllRequestsWithTasks, + setMigrationsFilterAction }) => (
@@ -39,6 +40,7 @@ const MigrationsInProgressCards = ({ isEditingPlanRequest={isEditingPlanRequest} isFetchingTransformationPlans={isFetchingTransformationPlans} isFetchingAllRequestsWithTasks={isFetchingAllRequestsWithTasks} + setMigrationsFilterAction={setMigrationsFilterAction} /> )) ) : ( @@ -67,7 +69,8 @@ MigrationsInProgressCards.propTypes = { acknowledgeDeniedPlanRequestAction: PropTypes.func, isEditingPlanRequest: PropTypes.bool, isFetchingTransformationPlans: PropTypes.bool, - isFetchingAllRequestsWithTasks: PropTypes.bool + isFetchingAllRequestsWithTasks: PropTypes.bool, + setMigrationsFilterAction: PropTypes.func }; MigrationsInProgressCards.defaultProps = { From 0100026feecda56271c3d9cfca4cc297b9dcb581 Mon Sep 17 00:00:00 2001 From: Mike Turley Date: Tue, 20 Nov 2018 18:26:22 -0500 Subject: [PATCH 10/11] Don't block button during polling --- app/javascript/react/screens/App/Overview/Overview.js | 2 -- .../App/Overview/components/Migrations/Migrations.js | 6 ------ .../components/Migrations/MigrationsInProgressCard.js | 6 +----- .../components/Migrations/MigrationsInProgressCards.js | 6 ------ 4 files changed, 1 insertion(+), 19 deletions(-) diff --git a/app/javascript/react/screens/App/Overview/Overview.js b/app/javascript/react/screens/App/Overview/Overview.js index a912cece32..afc5341bad 100644 --- a/app/javascript/react/screens/App/Overview/Overview.js +++ b/app/javascript/react/screens/App/Overview/Overview.js @@ -313,9 +313,7 @@ class Overview extends React.Component { fetchTransformationPlansAction={fetchTransformationPlansAction} fetchTransformationPlansUrl={fetchTransformationPlansUrl} fetchArchivedTransformationPlansUrl={fetchArchivedTransformationPlansUrl} - isFetchingTransformationPlans={isFetchingTransformationPlans} isFetchingArchivedTransformationPlans={isFetchingArchivedTransformationPlans} - isFetchingAllRequestsWithTasks={isFetchingAllRequestsWithTasks} archiveTransformationPlanAction={archiveTransformationPlanAction} archiveTransformationPlanUrl={archiveTransformationPlanUrl} deleteTransformationPlanAction={deleteTransformationPlanAction} diff --git a/app/javascript/react/screens/App/Overview/components/Migrations/Migrations.js b/app/javascript/react/screens/App/Overview/components/Migrations/Migrations.js index 81d32a99ae..1148113adc 100644 --- a/app/javascript/react/screens/App/Overview/components/Migrations/Migrations.js +++ b/app/javascript/react/screens/App/Overview/components/Migrations/Migrations.js @@ -51,9 +51,7 @@ class Migrations extends React.Component { fetchTransformationPlansAction, fetchTransformationPlansUrl, fetchArchivedTransformationPlansUrl, - isFetchingTransformationPlans, isFetchingArchivedTransformationPlans, - isFetchingAllRequestsWithTasks, archivedTransformationPlans, allArchivedPlanRequestsWithTasks, archiveTransformationPlanAction, @@ -145,8 +143,6 @@ class Migrations extends React.Component { fetchTransformationPlansUrl={fetchTransformationPlansUrl} acknowledgeDeniedPlanRequestAction={acknowledgeDeniedPlanRequestAction} isEditingPlanRequest={isEditingPlanRequest} - isFetchingTransformationPlans={isFetchingTransformationPlans} - isFetchingAllRequestsWithTasks={isFetchingAllRequestsWithTasks} setMigrationsFilterAction={setMigrationsFilterAction} /> )} @@ -225,9 +221,7 @@ Migrations.propTypes = { fetchArchivedTransformationPlansUrl: PropTypes.string, archivedTransformationPlans: PropTypes.array, allArchivedPlanRequestsWithTasks: PropTypes.array, - isFetchingTransformationPlans: PropTypes.bool, isFetchingArchivedTransformationPlans: PropTypes.string, - isFetchingAllRequestsWithTasks: PropTypes.bool, archiveTransformationPlanAction: PropTypes.func, archiveTransformationPlanUrl: PropTypes.string, deleteTransformationPlanAction: PropTypes.func, diff --git a/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCard.js b/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCard.js index ae8db7d79d..9ff215d5cf 100644 --- a/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCard.js +++ b/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCard.js @@ -31,8 +31,6 @@ const MigrationsInProgressCard = ({ fetchTransformationPlansUrl, acknowledgeDeniedPlanRequestAction, isEditingPlanRequest, - isFetchingTransformationPlans, - isFetchingAllRequestsWithTasks, setMigrationsFilterAction }) => { const requestsOfAssociatedPlan = allRequestsWithTasks.filter(request => request.source_id === plan.id); @@ -76,7 +74,7 @@ const MigrationsInProgressCard = ({ planRequest: mostRecentRequest }).then(() => setMigrationsFilterAction(MIGRATIONS_FILTERS.completed)) } - disabled={isEditingPlanRequest || isFetchingTransformationPlans || isFetchingAllRequestsWithTasks} + disabled={isEditingPlanRequest} > {__('Cancel Migration')} @@ -300,8 +298,6 @@ MigrationsInProgressCard.propTypes = { fetchTransformationPlansUrl: PropTypes.string, acknowledgeDeniedPlanRequestAction: PropTypes.func, isEditingPlanRequest: PropTypes.bool, - isFetchingTransformationPlans: PropTypes.bool, - isFetchingAllRequestsWithTasks: PropTypes.bool, setMigrationsFilterAction: PropTypes.func }; diff --git a/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCards.js b/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCards.js index c200dead57..03332c014d 100644 --- a/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCards.js +++ b/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCards.js @@ -14,8 +14,6 @@ const MigrationsInProgressCards = ({ fetchTransformationPlansUrl, acknowledgeDeniedPlanRequestAction, isEditingPlanRequest, - isFetchingTransformationPlans, - isFetchingAllRequestsWithTasks, setMigrationsFilterAction }) => (
@@ -38,8 +36,6 @@ const MigrationsInProgressCards = ({ fetchTransformationPlansUrl={fetchTransformationPlansUrl} acknowledgeDeniedPlanRequestAction={acknowledgeDeniedPlanRequestAction} isEditingPlanRequest={isEditingPlanRequest} - isFetchingTransformationPlans={isFetchingTransformationPlans} - isFetchingAllRequestsWithTasks={isFetchingAllRequestsWithTasks} setMigrationsFilterAction={setMigrationsFilterAction} /> )) @@ -68,8 +64,6 @@ MigrationsInProgressCards.propTypes = { fetchTransformationPlansUrl: PropTypes.string, acknowledgeDeniedPlanRequestAction: PropTypes.func, isEditingPlanRequest: PropTypes.bool, - isFetchingTransformationPlans: PropTypes.bool, - isFetchingAllRequestsWithTasks: PropTypes.bool, setMigrationsFilterAction: PropTypes.func }; From 7b6d4eaf785a40d11a2133161200bd6e895f21a7 Mon Sep 17 00:00:00 2001 From: Mike Turley Date: Tue, 20 Nov 2018 18:32:28 -0500 Subject: [PATCH 11/11] Update snapshot --- .../App/Overview/__test__/__snapshots__/index.test.js.snap | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/javascript/react/screens/App/Overview/__test__/__snapshots__/index.test.js.snap b/app/javascript/react/screens/App/Overview/__test__/__snapshots__/index.test.js.snap index cf91e1b93d..5a440bdbf6 100644 --- a/app/javascript/react/screens/App/Overview/__test__/__snapshots__/index.test.js.snap +++ b/app/javascript/react/screens/App/Overview/__test__/__snapshots__/index.test.js.snap @@ -2,6 +2,7 @@ exports[`Overview integration test should mount the Overview with mapStateToProps reduced 1`] = ` Object { + "acknowledgeDeniedPlanRequestAction": [Function], "activeTransformationPlans": Array [], "addNotificationAction": [Function], "archiveTransformationPlanAction": [Function], @@ -12,6 +13,7 @@ Object { "datastores": Array [], "deleteTransformationPlanAction": [Function], "deleteTransformationPlanUrl": "/api/service_templates", + "editPlanRequestAction": [Function], "fetchArchivedTransformationPlansUrl": "/api/dummyArchivedTransformationPlans", "fetchClustersUrl": "/api/dummyClusters", "fetchDatastoresUrl": "/api/dummyDatastores",