diff --git a/app/javascript/components/index.js b/app/javascript/components/index.js index c2ff8683ed..86c284f5ca 100644 --- a/app/javascript/components/index.js +++ b/app/javascript/components/index.js @@ -124,7 +124,7 @@ export const coreComponents = [ type: PlanContainer, data: { fetchPlanUrl: '/api/service_templates', - fetchPlanRequestUrl: '/api/service_requests' + fetchTasksForAllRequestsForPlanUrl: '/api/requests?expand=resource&attributes=miq_request_tasks' }, store: true }, 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 d842a65253..015e0ac2d6 100644 --- a/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsCompletedList.js +++ b/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsCompletedList.js @@ -4,6 +4,7 @@ import { noop, Button, ListView, Grid, Spinner, Icon } from 'patternfly-react'; import { IsoElapsedTime } from '../../../../../../components/dates/IsoElapsedTime'; import OverviewEmptyState from '../OverviewEmptyState/OverviewEmptyState'; import getMostRecentRequest from '../../../common/getMostRecentRequest'; +import getMostRecentVMTasksFromRequests from './helpers/getMostRecentVMTasksFromRequests'; const MigrationsCompletedList = ({ finishedTransformationPlans, @@ -33,11 +34,18 @@ const MigrationsCompletedList = ({ const failed = mostRecentRequest && mostRecentRequest.status === 'Error'; const tasks = {}; - if (mostRecentRequest) - mostRecentRequest.miq_request_tasks.forEach(task => { - tasks[task.source_id] = task.status === 'Ok'; - }); - + let tasksOfPlan = {}; + if (requestsOfAssociatedPlan.length > 0) { + tasksOfPlan = getMostRecentVMTasksFromRequests( + requestsOfAssociatedPlan, + plan.options.config_info.actions + ); + } else if (mostRecentRequest) { + tasksOfPlan = mostRecentRequest.miq_request_tasks; + } + tasksOfPlan.forEach(task => { + tasks[task.source_id] = task.status === 'Ok'; + }); let succeedCount = 0; Object.keys(tasks).forEach(key => { if (tasks[key]) succeedCount += 1; 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 be5e5e2b47..08f0e8d4cf 100644 --- a/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCard.js +++ b/app/javascript/react/screens/App/Overview/components/Migrations/MigrationsInProgressCard.js @@ -14,6 +14,7 @@ import { } from 'patternfly-react'; import TickingIsoElapsedTime from '../../../../../../components/dates/TickingIsoElapsedTime'; import getMostRecentRequest from '../../../common/getMostRecentRequest'; +import getMostRecentVMTasksFromRequests from './helpers/getMostRecentVMTasksFromRequests'; const MigrationsInProgressCard = ({ plan, allRequestsWithTasks, reloadCard, handleClick }) => { const requestsOfAssociatedPlan = allRequestsWithTasks.filter(request => request.source_id === plan.id); @@ -52,12 +53,16 @@ const MigrationsInProgressCard = ({ plan, allRequestsWithTasks, reloadCard, hand // UX business rule 2: aggregrate the tasks across requests reflecting current status of all tasks, // (gather the last status for the vm, gather the last storage for use in UX bussiness rule 3) const tasks = {}; - requestsOfAssociatedPlan.forEach(request => { - request.miq_request_tasks.forEach(task => { - tasks[task.source_id] = tasks[task.source_id] || {}; - tasks[task.source_id].completed = task.status === 'Ok' && task.state === 'finished'; - tasks[task.source_id].virtv2v_disks = task.options.virtv2v_disks; - }); + let tasksOfPlan = {}; + if (requestsOfAssociatedPlan.length > 0) { + tasksOfPlan = getMostRecentVMTasksFromRequests(requestsOfAssociatedPlan, plan.options.config_info.actions); + } else if (mostRecentRequest) { + tasksOfPlan = mostRecentRequest.miq_request_tasks; + } + tasksOfPlan.forEach(task => { + tasks[task.source_id] = tasks[task.source_id] || {}; + tasks[task.source_id].completed = task.status === 'Ok' && task.state === 'finished'; + tasks[task.source_id].virtv2v_disks = task.options.virtv2v_disks; }); let completedVMs = 0; diff --git a/app/javascript/react/screens/App/Overview/components/Migrations/helpers/getMostRecentVMTasksFromRequests.js b/app/javascript/react/screens/App/Overview/components/Migrations/helpers/getMostRecentVMTasksFromRequests.js new file mode 100644 index 0000000000..fc612de5b8 --- /dev/null +++ b/app/javascript/react/screens/App/Overview/components/Migrations/helpers/getMostRecentVMTasksFromRequests.js @@ -0,0 +1,24 @@ +import commonUtilitiesHelper from '../../../../common/commonUtilitiesHelper'; + +const getMostRecentVMTasksFromRequests = (requests, actions) => { + const vm_ids = actions.map(a => a.vm_id); + + const allTasks = requests.map(request => request.miq_request_tasks); + + const flattenAllTasks = []; + commonUtilitiesHelper.flatten(allTasks, flattenAllTasks); + + const groupedByVMId = commonUtilitiesHelper.groupBy(flattenAllTasks, 'source_id'); + + const vmTasksForRequestOfPlan = []; + vmTasksForRequestOfPlan.push( + vm_ids.map(vmId => commonUtilitiesHelper.getMostRecentEntityByCreationDate(groupedByVMId[vmId])) + ); + + const flattenVMTasksForRequestOfPlan = []; + commonUtilitiesHelper.flatten(vmTasksForRequestOfPlan, flattenVMTasksForRequestOfPlan); + + return flattenVMTasksForRequestOfPlan; +}; + +export default getMostRecentVMTasksFromRequests; diff --git a/app/javascript/react/screens/App/Overview/overview.requestWithTasks.fixtures.js b/app/javascript/react/screens/App/Overview/overview.requestWithTasks.fixtures.js index 889bbd61b6..94f4110b81 100644 --- a/app/javascript/react/screens/App/Overview/overview.requestWithTasks.fixtures.js +++ b/app/javascript/react/screens/App/Overview/overview.requestWithTasks.fixtures.js @@ -221,7 +221,7 @@ export const allRequestsWithTasks = Immutable({ phase_context: {}, request_state: 'Finished', request_type: 'transformation_plan', - source_id: '26', + source_id: '1', source_type: 'VmOrTemplate', state: 'finished', status: 'Error', @@ -556,7 +556,7 @@ export const allRequestsWithTasks = Immutable({ phase_context: {}, request_state: '', request_type: 'transformation_plan', - source_id: '26', + source_id: '3', source_type: 'VmOrTemplate', state: 'active', status: 'Ok', @@ -962,7 +962,7 @@ export const allRequestsWithTasks = Immutable({ phase_context: {}, request_state: 'Finished', request_type: 'transformation_plan', - source_id: '26', + source_id: '1', source_type: 'VmOrTemplate', state: 'finished', status: 'Ok', @@ -1156,7 +1156,7 @@ export const allRequestsWithTasks = Immutable({ phase_context: {}, request_state: 'Finished', request_type: 'transformation_plan', - source_id: '26', + source_id: '3', source_type: 'VmOrTemplate', state: 'finished', status: '', @@ -1369,7 +1369,7 @@ export const allRequestsWithTasks = Immutable({ phase_context: {}, request_state: 'Finished', request_type: 'transformation_plan', - source_id: '26', + source_id: '1', source_type: 'VmOrTemplate', state: 'finished', status: 'Ok', @@ -1563,7 +1563,7 @@ export const allRequestsWithTasks = Immutable({ phase_context: {}, request_state: 'Finished', request_type: 'transformation_plan', - source_id: '26', + source_id: '3', source_type: 'VmOrTemplate', state: 'finished', status: 'Ok', @@ -1969,7 +1969,7 @@ export const allRequestsWithTasks = Immutable({ phase_context: {}, request_state: 'Finished', request_type: 'transformation_plan', - source_id: '26', + source_id: '1', source_type: 'VmOrTemplate', state: 'finished', status: 'Error', diff --git a/app/javascript/react/screens/App/Overview/overview.transformationPlans.fixtures.js b/app/javascript/react/screens/App/Overview/overview.transformationPlans.fixtures.js index d5e8bd3629..ed1743a08e 100644 --- a/app/javascript/react/screens/App/Overview/overview.transformationPlans.fixtures.js +++ b/app/javascript/react/screens/App/Overview/overview.transformationPlans.fixtures.js @@ -17,7 +17,7 @@ export const transformationPlans = Immutable({ actions: [{ vm_id: '1' }, { vm_id: '3' }] } }, - created_at: '2018-05-01T12:15:00Z', + created_on: '2018-05-01T12:15:00Z', id: '10', miq_requests: [] }, @@ -81,7 +81,7 @@ export const transformationPlans = Immutable({ actions: [{ vm_id: '1' }, { vm_id: '3' }] } }, - created_at: '2018-05-01T12:13:50Z', + created_on: '2018-05-01T12:13:50Z', id: '30', miq_requests: [ { @@ -166,7 +166,7 @@ export const transformationPlans = Immutable({ actions: [{ vm_id: '1' }, { vm_id: '3' }] } }, - created_at: '2018-05-01T12:13:50', + created_on: '2018-05-01T12:13:50', id: '40', miq_requests: [ { @@ -217,7 +217,7 @@ export const transformationPlans = Immutable({ actions: [{ vm_id: '1' }, { vm_id: '3' }] } }, - created_at: '2018-05-01T12:13:50Z', + created_on: '2018-05-01T12:13:50Z', id: '50', miq_requests: [ { @@ -268,7 +268,7 @@ export const transformationPlans = Immutable({ actions: [{ vm_id: '1' }, { vm_id: '3' }] } }, - created_at: '2018-05-01T12:13:50Z', + created_on: '2018-05-01T12:13:50Z', id: '60', miq_requests: [ { diff --git a/app/javascript/react/screens/App/Plan/Plan.js b/app/javascript/react/screens/App/Plan/Plan.js index 7982eea4af..0b07e45ad1 100644 --- a/app/javascript/react/screens/App/Plan/Plan.js +++ b/app/javascript/react/screens/App/Plan/Plan.js @@ -43,8 +43,8 @@ class Plan extends React.Component { fetchPlanUrl, fetchPlanAction, planId, - fetchPlanRequestUrl, - fetchPlanRequestAction, + fetchTasksForAllRequestsForPlanUrl, + fetchTasksForAllRequestsForPlanAction, queryPlanVmsAction } = this.props; @@ -63,10 +63,9 @@ class Plan extends React.Component { if (miq_requests.length > 0) { const mostRecentRequest = getMostRecentRequest(miq_requests); - const planRequestId = mostRecentRequest.id; - fetchPlanRequestAction(fetchPlanRequestUrl, planRequestId); + fetchTasksForAllRequestsForPlanAction(fetchTasksForAllRequestsForPlanUrl, miq_requests); if (mostRecentRequest.request_state === 'active') { - this.startPolling(planRequestId); + this.startPolling(miq_requests); } else { this.setState(() => ({ planFinished: true @@ -84,10 +83,10 @@ class Plan extends React.Component { resetPlanStateAction(); } - startPolling = id => { - const { fetchPlanRequestAction, fetchPlanRequestUrl } = this.props; + startPolling = requests => { + const { fetchTasksForAllRequestsForPlanAction, fetchTasksForAllRequestsForPlanUrl } = this.props; this.pollingInterval = setInterval(() => { - fetchPlanRequestAction(fetchPlanRequestUrl, id); + fetchTasksForAllRequestsForPlanAction(fetchTasksForAllRequestsForPlanUrl, requests); }, 15000); }; @@ -198,8 +197,8 @@ class Plan extends React.Component { } } Plan.propTypes = { - fetchPlanRequestUrl: PropTypes.string.isRequired, - fetchPlanRequestAction: PropTypes.func.isRequired, + fetchTasksForAllRequestsForPlanUrl: PropTypes.string.isRequired, + fetchTasksForAllRequestsForPlanAction: PropTypes.func.isRequired, planName: PropTypes.string, planRequestFailed: PropTypes.bool, planArchived: PropTypes.bool, diff --git a/app/javascript/react/screens/App/Plan/PlanActions.js b/app/javascript/react/screens/App/Plan/PlanActions.js index 6a6a7f6903..0fc874dbee 100644 --- a/app/javascript/react/screens/App/Plan/PlanActions.js +++ b/app/javascript/react/screens/App/Plan/PlanActions.js @@ -4,8 +4,8 @@ import API from '../../../../common/API'; import http from '../../../../common/http'; import { - FETCH_V2V_PLAN_REQUEST, FETCH_V2V_PLAN, + FETCH_V2V_ALL_REQUESTS_WITH_TASKS_FOR_PLAN, QUERY_V2V_PLAN_VMS, RESET_PLAN_STATE, FETCH_V2V_MIGRATION_TASK_LOG, @@ -16,20 +16,27 @@ import { import { V2V_NOTIFICATION_ADD } from '../common/NotificationList/NotificationConstants'; // ***************************************************************************** -// * FETCH_V2V_PLAN_REQUEST +// * FETCH_V2V_ALL_REQUESTS_WITH_TASKS_FOR_PLAN // ***************************************************************************** -const _getPlanRequestActionCreator = url => dispatch => +const _getTasksForAllRequestsForPlanActionCreator = (url, allRequests) => dispatch => { dispatch({ - type: FETCH_V2V_PLAN_REQUEST, - payload: API.get(url) + type: FETCH_V2V_ALL_REQUESTS_WITH_TASKS_FOR_PLAN, + payload: new Promise((resolve, reject) => { + API.post(url, { + action: 'query', + resources: allRequests + }) + .then(response => { + resolve(response); + }) + .catch(e => reject(e)); + }) }); - -export const fetchPlanRequestAction = (url, id) => { - const uri = new URI(`${url}/${id}`); - uri.addSearch({ attributes: 'miq_request_tasks' }); - return _getPlanRequestActionCreator(uri.toString()); }; +export const fetchTasksForAllRequestsForPlanAction = (url, allRequests) => + _getTasksForAllRequestsForPlanActionCreator(url, allRequests); + // ***************************************************************************** // * QUERY_V2V_PLAN_VMS // ***************************************************************************** diff --git a/app/javascript/react/screens/App/Plan/PlanConstants.js b/app/javascript/react/screens/App/Plan/PlanConstants.js index 3efc6c6790..2d5cb6c224 100644 --- a/app/javascript/react/screens/App/Plan/PlanConstants.js +++ b/app/javascript/react/screens/App/Plan/PlanConstants.js @@ -1,5 +1,6 @@ export const FETCH_V2V_PLAN_REQUEST = 'FETCH_V2V_PLAN_REQUEST'; export const FETCH_V2V_PLAN = 'FETCH_V2V_PLAN'; +export const FETCH_V2V_ALL_REQUESTS_WITH_TASKS_FOR_PLAN = 'FETCH_V2V_ALL_REQUESTS_WITH_TASKS_FOR_PLAN'; export const QUERY_V2V_PLAN_VMS = 'QUERY_V2V_PLAN_VMS'; export const RESET_PLAN_STATE = 'RESET_PLAN_STATE'; export const FETCH_V2V_MIGRATION_TASK_LOG = 'FETCH_V2V_MIGRATION_TASK_LOG'; diff --git a/app/javascript/react/screens/App/Plan/PlanReducer.js b/app/javascript/react/screens/App/Plan/PlanReducer.js index dc08e9a610..4e496c1dae 100644 --- a/app/javascript/react/screens/App/Plan/PlanReducer.js +++ b/app/javascript/react/screens/App/Plan/PlanReducer.js @@ -1,9 +1,11 @@ import Immutable from 'seamless-immutable'; import numeral from 'numeral'; +import commonUtilitiesHelper from '../common/commonUtilitiesHelper'; +import getMostRecentVMTasksFromRequests from '../Overview/components/Migrations/helpers/getMostRecentVMTasksFromRequests'; import { - FETCH_V2V_PLAN_REQUEST, FETCH_V2V_PLAN, + FETCH_V2V_ALL_REQUESTS_WITH_TASKS_FOR_PLAN, QUERY_V2V_PLAN_VMS, RESET_PLAN_STATE, FETCH_V2V_MIGRATION_TASK_LOG, @@ -35,102 +37,75 @@ const excludeDownloadDoneTaskId = (allDownloadLogInProgressTaskIds, taskId) => const includeDownloadInProgressTaskId = (allDownloadLogInProgressTaskIds, taskId) => allDownloadLogInProgressTaskIds ? allDownloadLogInProgressTaskIds.concat(taskId) : [taskId]; -const _formatPlanRequestDetails = data => { +const processVMTasks = vmTasks => { const tasks = []; - if (data.miq_request_tasks && data.miq_request_tasks.length) { - data.miq_request_tasks.forEach(task => { - const taskDetails = { - id: task.id, - message: task.message, - transformation_host_name: task.options && task.options.transformation_host_name, - delivered_on: new Date(task.options.delivered_on), - updated_on: new Date(task.updated_on), - completed: task.message === 'VM Transformations completed' || task.message === 'VM Transformations failed', - state: task.state, - status: task.status, - options: {} - }; - - taskDetails.options.progress = task.options.progress; - taskDetails.options.virtv2v_wrapper = task.options.virtv2v_wrapper; - - if (!task.diskSpaceCompletedGb) { - taskDetails.diskSpaceCompletedGb = '0'; - } + vmTasks.forEach(task => { + const taskDetails = { + id: task.id, + message: task.message, + transformation_host_name: task.options && task.options.transformation_host_name, + delivered_on: new Date(task.options.delivered_on), + updated_on: new Date(task.updated_on), + completed: task.message === 'VM Transformations completed' || task.message === 'VM Transformations failed', + state: task.state, + status: task.status, + options: {} + }; + + taskDetails.options.progress = task.options.progress; + taskDetails.options.virtv2v_wrapper = task.options.virtv2v_wrapper; + + if (!task.diskSpaceCompletedGb) { + taskDetails.diskSpaceCompletedGb = '0'; + } - if (!task.percentComplete) { - taskDetails.percentComplete = 0; - } + if (!task.percentComplete) { + taskDetails.percentComplete = 0; + } - if (!task.totalDiskSpaceGb) { - taskDetails.totalDiskSpaceGb = '100%'; - } + if (!task.totalDiskSpaceGb) { + taskDetails.totalDiskSpaceGb = '100%'; + } - const grepVMName = task.description.match(/\[(.*?)\]/); + const grepVMName = task.description.match(/\[(.*?)\]/); - if (grepVMName) { - [taskDetails.descriptionPrefix, taskDetails.vmName] = grepVMName; - } + if (grepVMName) { + [taskDetails.descriptionPrefix, taskDetails.vmName] = grepVMName; + } - const startDateTime = taskDetails.delivered_on; - const lastUpdateDateTime = taskDetails.updated_on; - taskDetails.startDateTime = startDateTime; - taskDetails.lastUpdateDateTime = lastUpdateDateTime; + const startDateTime = taskDetails.delivered_on; + const lastUpdateDateTime = taskDetails.updated_on; + taskDetails.startDateTime = startDateTime; + taskDetails.lastUpdateDateTime = lastUpdateDateTime; - if (taskDetails.completed) { - taskDetails.completedSuccessfully = - task.options.progress.current_description === 'Virtual machine migrated' || - task.options.progress.current_description === 'Mark source as migrated'; - } - if (task.options && task.options.virtv2v_disks && task.options.virtv2v_disks.length) { - taskDetails.totalDiskSpace = task.options.virtv2v_disks.reduce((a, b) => a + b.size, 0); - taskDetails.totalDiskSpaceGb = numeral(taskDetails.totalDiskSpace).format('0.00b'); + if (taskDetails.completed) { + taskDetails.completedSuccessfully = + task.options.progress.current_description === 'Virtual machine migrated' || + task.options.progress.current_description === 'Mark source as migrated'; + } + if (task.options && task.options.virtv2v_disks && task.options.virtv2v_disks.length) { + taskDetails.totalDiskSpace = task.options.virtv2v_disks.reduce((a, b) => a + b.size, 0); + taskDetails.totalDiskSpaceGb = numeral(taskDetails.totalDiskSpace).format('0.00b'); - const percentComplete = - task.options.virtv2v_disks.reduce((a, b) => a + b.percent, 0) / (100 * task.options.virtv2v_disks.length); + const percentComplete = + task.options.virtv2v_disks.reduce((a, b) => a + b.percent, 0) / (100 * task.options.virtv2v_disks.length); - taskDetails.diskSpaceCompleted = percentComplete * taskDetails.totalDiskSpace; - taskDetails.diskSpaceCompletedGb = numeral(taskDetails.diskSpaceCompleted).format('0.00b'); - taskDetails.percentComplete = Math.round(percentComplete * 1000) / 10; - } - tasks.push(taskDetails); - }); - } + taskDetails.diskSpaceCompleted = percentComplete * taskDetails.totalDiskSpace; + taskDetails.diskSpaceCompletedGb = numeral(taskDetails.diskSpaceCompleted).format('0.00b'); + taskDetails.percentComplete = Math.round(percentComplete * 1000) / 10; + } + tasks.push(taskDetails); + }); return tasks; }; -const _deepCompare = (prevTasks, newTasks) => JSON.stringify(prevTasks) === JSON.stringify(newTasks); +const allVMTasksForRequestOfPlan = (requestWithTasks, actions) => { + const tasksOfPlan = getMostRecentVMTasksFromRequests(requestWithTasks, actions); + return processVMTasks(tasksOfPlan); +}; export default (state = initialState, action) => { switch (action.type) { - case `${FETCH_V2V_PLAN_REQUEST}_PENDING`: - return state.set('isFetchingPlanRequest', true).set('isRejectedPlanRequest', false); - case `${FETCH_V2V_PLAN_REQUEST}_FULFILLED`: { - const { payload } = action; - if (payload.data) { - const newTasks = _formatPlanRequestDetails(payload.data); - if (!_deepCompare(state.planRequestTasks, newTasks)) { - return state - .set('planRequestPreviouslyFetched', true) - .set('planRequestTasks', newTasks) - .set('planRequestFailed', payload.data.status === 'Error') - .set('isRejectedPlanRequest', false) - .set('errorPlanRequest', null) - .set('isFetchingPlanRequest', false); - } - } - return state - .set('planRequestPreviouslyFetched', true) - .set('isRejectedPlanRequest', false) - .set('errorPlanRequest', null) - .set('isFetchingPlanRequest', false); - } - case `${FETCH_V2V_PLAN_REQUEST}_REJECTED`: - return state - .set('errorPlanRequest', action.payload) - .set('isRejectedPlanRequest', true) - .set('isFetchingPlanRequest', false); - case `${FETCH_V2V_PLAN}_PENDING`: return state.set('isFetchingPlan', true).set('isRejectedPlan', false); case `${FETCH_V2V_PLAN}_FULFILLED`: @@ -147,6 +122,36 @@ export default (state = initialState, action) => { .set('isRejectedPlan', true) .set('errorPlan', action.payload); + 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) { + return state + .set( + 'planRequestTasks', + allVMTasksForRequestOfPlan(action.payload.data.results, 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' + ) + .set('isRejectedPlanRequest', false) + .set('errorPlanRequest', null) + .set('isFetchingPlanRequest', false); + } + return state + .set('planRequestPreviouslyFetched', true) + .set('isRejectedPlanRequest', false) + .set('errorPlanRequest', null) + .set('isFetchingPlanRequest', false); + case `${FETCH_V2V_ALL_REQUESTS_WITH_TASKS_FOR_PLAN}_REJECTED`: + return state + .set('errorPlanRequest', action.payload) + .set('isRejectedPlanRequest', true) + .set('isFetchingPlanRequest', false); + case `${QUERY_V2V_PLAN_VMS}_PENDING`: return state.set('isQueryingVms', true).set('isRejectedVms', false); case `${QUERY_V2V_PLAN_VMS}_FULFILLED`: diff --git a/app/javascript/react/screens/App/common/commonUtilitiesHelper.js b/app/javascript/react/screens/App/common/commonUtilitiesHelper.js new file mode 100644 index 0000000000..1559bf1d74 --- /dev/null +++ b/app/javascript/react/screens/App/common/commonUtilitiesHelper.js @@ -0,0 +1,18 @@ +const commonUtilitiesHelper = { + getMostRecentEntityByCreationDate: entities => + entities.reduce((prev, current) => (prev.created_on > current.created_on ? prev : current)), + + groupBy: (items, key) => + items.reduce( + (result, item) => ({ + ...result, + [item[key]]: [...(result[item[key]] || []), item] + }), + {} + ), + + flatten: (arrayItems, flattenedArray) => + arrayItems.map(task => task.map(arrayElement => flattenedArray.push(arrayElement))) +}; + +export default commonUtilitiesHelper;