Fetch full resource object and replace the matching node

This commit is contained in:
Marliana Lara
2020-04-01 15:21:42 -04:00
parent c18aa90534
commit 4704e24c24
5 changed files with 108 additions and 25 deletions

View File

@@ -28,12 +28,16 @@ function hasPromptData(launchData) {
} }
function formatTimeout(timeout) { function formatTimeout(timeout) {
if (typeof timeout === "undefined" || timeout === null) { if (typeof timeout === 'undefined' || timeout === null) {
return null; return null;
} }
const minutes = Math.floor(timeout / 60); const minutes = Math.floor(timeout / 60);
const seconds = timeout - Math.floor(timeout / 60) * 60; const seconds = timeout - Math.floor(timeout / 60) * 60;
return <>{minutes} <Trans>min</Trans> {seconds} <Trans>sec</Trans></>; return (
<>
{minutes} <Trans>min</Trans> {seconds} <Trans>sec</Trans>
</>
);
} }
function PromptDetail({ i18n, resource, launchConfig = {} }) { function PromptDetail({ i18n, resource, launchConfig = {} }) {

View File

@@ -17,6 +17,7 @@ export function initReducer() {
nodes: [], nodes: [],
nodeToDelete: null, nodeToDelete: null,
nodeToEdit: null, nodeToEdit: null,
nodeToView: null,
showDeleteAllNodesModal: false, showDeleteAllNodesModal: false,
showLegend: false, showLegend: false,
showTools: false, showTools: false,
@@ -93,6 +94,8 @@ export default function visualizerReducer(state, action) {
return updateLink(state, action.linkType); return updateLink(state, action.linkType);
case 'UPDATE_NODE': case 'UPDATE_NODE':
return updateNode(state, action.node); return updateNode(state, action.node);
case 'REFRESH_NODE':
return refreshNode(state, action.node);
default: default:
throw new Error(`Unrecognized action type: ${action.type}`); throw new Error(`Unrecognized action type: ${action.type}`);
} }
@@ -607,3 +610,17 @@ function updateNode(state, editedNode) {
unsavedChanges: true, unsavedChanges: true,
}; };
} }
function refreshNode(state, refreshedNode) {
const { nodeToView, nodes } = state;
const newNodes = [...nodes];
const matchingNode = newNodes.find(node => node.id === nodeToView.id);
matchingNode.unifiedJobTemplate = refreshedNode.nodeResource;
return {
...state,
nodes: newNodes,
nodeToView: matchingNode,
};
}

View File

@@ -16,6 +16,7 @@ const defaultState = {
nodes: [], nodes: [],
nodeToDelete: null, nodeToDelete: null,
nodeToEdit: null, nodeToEdit: null,
nodeToView: null,
showDeleteAllNodesModal: false, showDeleteAllNodesModal: false,
showLegend: false, showLegend: false,
showTools: false, showTools: false,

View File

@@ -11,44 +11,105 @@ import ContentError from '@components/ContentError';
import ContentLoading from '@components/ContentLoading'; import ContentLoading from '@components/ContentLoading';
import PromptDetail from '@components/PromptDetail'; import PromptDetail from '@components/PromptDetail';
import useRequest from '@util/useRequest'; import useRequest from '@util/useRequest';
import { JobTemplatesAPI, WorkflowJobTemplatesAPI } from '@api'; import {
InventorySourcesAPI,
JobTemplatesAPI,
ProjectsAPI,
WorkflowJobTemplatesAPI,
} from '@api';
function getNodeType(node) {
const ujtType = node.type || node.unified_job_type;
let nodeType;
let nodeAPI;
switch (ujtType) {
case 'job_template':
case 'job':
nodeType = 'job_template';
nodeAPI = JobTemplatesAPI;
break;
case 'project':
case 'project_update':
nodeType = 'project_sync';
nodeAPI = ProjectsAPI;
break;
case 'inventory_source':
case 'inventory_update':
nodeType = 'inventory_source_sync';
nodeAPI = InventorySourcesAPI;
break;
case 'workflow_job_template':
case 'workflow_job':
nodeType = 'workflow_job_template';
nodeAPI = WorkflowJobTemplatesAPI;
break;
case 'workflow_approval_template':
case 'workflow_approval':
nodeType = 'approval';
nodeAPI = null;
break;
default:
}
return [nodeType, nodeAPI];
}
function NodeViewModal({ i18n }) { function NodeViewModal({ i18n }) {
const dispatch = useContext(WorkflowDispatchContext); const dispatch = useContext(WorkflowDispatchContext);
const { nodeToView } = useContext(WorkflowStateContext); const { nodeToView } = useContext(WorkflowStateContext);
const { unifiedJobTemplate } = nodeToView; const { unifiedJobTemplate } = nodeToView;
const jobType = const [nodeType, nodeAPI] = getNodeType(unifiedJobTemplate);
unifiedJobTemplate.unified_job_type || unifiedJobTemplate.type;
const { const {
result: launchConfig, result: launchConfig,
isLoading, isLoading: isLaunchConfigLoading,
error, error: launchConfigError,
request: fetchLaunchConfig, request: fetchLaunchConfig,
} = useRequest( } = useRequest(
useCallback(async () => { useCallback(async () => {
const readLaunch = ['workflow_job', 'workflow_job_template'].includes( const readLaunch =
jobType nodeType === 'workflow_job_template'
) ? WorkflowJobTemplatesAPI.readLaunch(unifiedJobTemplate.id)
? WorkflowJobTemplatesAPI.readLaunch(unifiedJobTemplate.id) : JobTemplatesAPI.readLaunch(unifiedJobTemplate.id);
: JobTemplatesAPI.readLaunch(unifiedJobTemplate.id);
const { data } = await readLaunch; const { data } = await readLaunch;
return data; return data;
}, [jobType, unifiedJobTemplate]), }, [nodeType, unifiedJobTemplate.id]),
{} {}
); );
const {
result: nodeDetail,
isLoading: isNodeDetailLoading,
error: nodeDetailError,
request: fetchNodeDetail,
} = useRequest(
useCallback(async () => {
const { data } = await nodeAPI?.readDetail(unifiedJobTemplate.id);
return data;
}, [nodeAPI, unifiedJobTemplate.id]),
null
);
useEffect(() => { useEffect(() => {
if ( if (nodeType === 'workflow_job_template' || nodeType === 'job_template') {
['workflow_job', 'workflow_job_template', 'job', 'job_template'].includes(
jobType
)
) {
fetchLaunchConfig(); fetchLaunchConfig();
} }
}, [jobType, fetchLaunchConfig]);
if (unifiedJobTemplate.unified_job_type && nodeType !== 'approval') {
fetchNodeDetail();
}
}, []); // eslint-disable-line react-hooks/exhaustive-deps
useEffect(() => {
if (nodeDetail) {
dispatch({
type: 'REFRESH_NODE',
node: {
nodeResource: nodeDetail,
},
});
}
}, [nodeDetail]); // eslint-disable-line react-hooks/exhaustive-deps
const handleEdit = () => { const handleEdit = () => {
dispatch({ type: 'SET_NODE_TO_VIEW', value: null }); dispatch({ type: 'SET_NODE_TO_VIEW', value: null });
@@ -56,11 +117,10 @@ function NodeViewModal({ i18n }) {
}; };
let Content; let Content;
if (isLaunchConfigLoading || isNodeDetailLoading) {
if (isLoading) {
Content = <ContentLoading />; Content = <ContentLoading />;
} else if (error) { } else if (launchConfigError || nodeDetailError) {
Content = <ContentError error={error} />; Content = <ContentError error={launchConfigError || nodeDetailError} />;
} else { } else {
Content = ( Content = (
<PromptDetail launchConfig={launchConfig} resource={unifiedJobTemplate} /> <PromptDetail launchConfig={launchConfig} resource={unifiedJobTemplate} />

View File

@@ -11,6 +11,7 @@ import NodeViewModal from './NodeViewModal';
jest.mock('@api/models/JobTemplates'); jest.mock('@api/models/JobTemplates');
jest.mock('@api/models/WorkflowJobTemplates'); jest.mock('@api/models/WorkflowJobTemplates');
WorkflowJobTemplatesAPI.readLaunch.mockResolvedValue({}); WorkflowJobTemplatesAPI.readLaunch.mockResolvedValue({});
WorkflowJobTemplatesAPI.readDetail.mockResolvedValue({});
JobTemplatesAPI.readLaunch.mockResolvedValue({}); JobTemplatesAPI.readLaunch.mockResolvedValue({});
const dispatch = jest.fn(); const dispatch = jest.fn();