mirror of
https://github.com/ansible/awx.git
synced 2026-05-17 14:27:42 -02:30
Refresh nodes after workflow has finished running so that we can display all job info for relevant nodes.
This commit is contained in:
@@ -33,10 +33,11 @@ const StyledExclamationTriangleIcon = styled(ExclamationTriangleIcon)`
|
|||||||
|
|
||||||
function WorkflowNodeHelp({ node, i18n }) {
|
function WorkflowNodeHelp({ node, i18n }) {
|
||||||
let nodeType;
|
let nodeType;
|
||||||
if (node.unifiedJobTemplate || node.job) {
|
const job = node?.originalNodeObject?.summary_fields?.job;
|
||||||
|
if (node.unifiedJobTemplate || job) {
|
||||||
const type = node.unifiedJobTemplate
|
const type = node.unifiedJobTemplate
|
||||||
? node.unifiedJobTemplate.unified_job_type || node.unifiedJobTemplate.type
|
? node.unifiedJobTemplate.unified_job_type || node.unifiedJobTemplate.type
|
||||||
: node.job.type;
|
: job.type;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'job_template':
|
case 'job_template':
|
||||||
case 'job':
|
case 'job':
|
||||||
@@ -64,8 +65,8 @@ function WorkflowNodeHelp({ node, i18n }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let jobStatus;
|
let jobStatus;
|
||||||
if (node.job) {
|
if (job) {
|
||||||
switch (node.job.status) {
|
switch (job.status) {
|
||||||
case 'new':
|
case 'new':
|
||||||
jobStatus = i18n._(t`New`);
|
jobStatus = i18n._(t`New`);
|
||||||
break;
|
break;
|
||||||
@@ -112,23 +113,22 @@ function WorkflowNodeHelp({ node, i18n }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!node.unifiedJobTemplate &&
|
{!node.unifiedJobTemplate && (!job || job.type !== 'workflow_approval') && (
|
||||||
(!node.job || node.job.type !== 'workflow_approval') && (
|
<>
|
||||||
<>
|
<ResourceDeleted job={job}>
|
||||||
<ResourceDeleted job={node.job}>
|
<StyledExclamationTriangleIcon />
|
||||||
<StyledExclamationTriangleIcon />
|
<Trans>
|
||||||
<Trans>
|
The resource associated with this node has been deleted.
|
||||||
The resource associated with this node has been deleted.
|
</Trans>
|
||||||
</Trans>
|
</ResourceDeleted>
|
||||||
</ResourceDeleted>
|
</>
|
||||||
</>
|
)}
|
||||||
)}
|
{job && (
|
||||||
{node.job && (
|
|
||||||
<GridDL>
|
<GridDL>
|
||||||
<dt>
|
<dt>
|
||||||
<b>{i18n._(t`Name`)}</b>
|
<b>{i18n._(t`Name`)}</b>
|
||||||
</dt>
|
</dt>
|
||||||
<dd id="workflow-node-help-name">{node.job.name}</dd>
|
<dd id="workflow-node-help-name">{job.name}</dd>
|
||||||
<dt>
|
<dt>
|
||||||
<b>{i18n._(t`Type`)}</b>
|
<b>{i18n._(t`Type`)}</b>
|
||||||
</dt>
|
</dt>
|
||||||
@@ -137,19 +137,19 @@ function WorkflowNodeHelp({ node, i18n }) {
|
|||||||
<b>{i18n._(t`Job Status`)}</b>
|
<b>{i18n._(t`Job Status`)}</b>
|
||||||
</dt>
|
</dt>
|
||||||
<dd id="workflow-node-help-status">{jobStatus}</dd>
|
<dd id="workflow-node-help-status">{jobStatus}</dd>
|
||||||
{typeof node.job.elapsed === 'number' && (
|
{typeof job.elapsed === 'number' && (
|
||||||
<>
|
<>
|
||||||
<dt>
|
<dt>
|
||||||
<b>{i18n._(t`Elapsed`)}</b>
|
<b>{i18n._(t`Elapsed`)}</b>
|
||||||
</dt>
|
</dt>
|
||||||
<dd id="workflow-node-help-elapsed">
|
<dd id="workflow-node-help-elapsed">
|
||||||
{secondsToHHMMSS(node.job.elapsed)}
|
{secondsToHHMMSS(job.elapsed)}
|
||||||
</dd>
|
</dd>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</GridDL>
|
</GridDL>
|
||||||
)}
|
)}
|
||||||
{node.unifiedJobTemplate && !node.job && (
|
{node.unifiedJobTemplate && !job && (
|
||||||
<GridDL>
|
<GridDL>
|
||||||
<dt>
|
<dt>
|
||||||
<b>{i18n._(t`Name`)}</b>
|
<b>{i18n._(t`Name`)}</b>
|
||||||
@@ -161,7 +161,7 @@ function WorkflowNodeHelp({ node, i18n }) {
|
|||||||
<dd id="workflow-node-help-type">{nodeType}</dd>
|
<dd id="workflow-node-help-type">{nodeType}</dd>
|
||||||
</GridDL>
|
</GridDL>
|
||||||
)}
|
)}
|
||||||
{node.job && node.job.type !== 'workflow_approval' && (
|
{job && job.type !== 'workflow_approval' && (
|
||||||
<p css="margin-top: 10px">{i18n._(t`Click to view job details`)}</p>
|
<p css="margin-top: 10px">{i18n._(t`Click to view job details`)}</p>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -365,9 +365,6 @@ function generateNodes(workflowNodes, i18n) {
|
|||||||
originalNodeObject: node,
|
originalNodeObject: node,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (node.summary_fields.job) {
|
|
||||||
nodeObj.job = node.summary_fields.job;
|
|
||||||
}
|
|
||||||
if (node.summary_fields.unified_job_template) {
|
if (node.summary_fields.unified_job_template) {
|
||||||
nodeObj.unifiedJobTemplate = node.summary_fields.unified_job_template;
|
nodeObj.unifiedJobTemplate = node.summary_fields.unified_job_template;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,24 +64,25 @@ Elapsed.displayName = 'Elapsed';
|
|||||||
function WorkflowOutputNode({ i18n, mouseEnter, mouseLeave, node }) {
|
function WorkflowOutputNode({ i18n, mouseEnter, mouseLeave, node }) {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { nodePositions } = useContext(WorkflowStateContext);
|
const { nodePositions } = useContext(WorkflowStateContext);
|
||||||
|
const job = node?.originalNodeObject?.summary_fields?.job;
|
||||||
let borderColor = '#93969A';
|
let borderColor = '#93969A';
|
||||||
|
|
||||||
if (node.job) {
|
if (job) {
|
||||||
if (
|
if (
|
||||||
node.job.status === 'failed' ||
|
job.status === 'failed' ||
|
||||||
node.job.status === 'error' ||
|
job.status === 'error' ||
|
||||||
node.job.status === 'canceled'
|
job.status === 'canceled'
|
||||||
) {
|
) {
|
||||||
borderColor = '#d9534f';
|
borderColor = '#d9534f';
|
||||||
}
|
}
|
||||||
if (node.job.status === 'successful' || node.job.status === 'ok') {
|
if (job.status === 'successful' || job.status === 'ok') {
|
||||||
borderColor = '#5cb85c';
|
borderColor = '#5cb85c';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleNodeClick = () => {
|
const handleNodeClick = () => {
|
||||||
if (node.job && node.job.type !== 'workflow_aproval') {
|
if (job && job.type !== 'workflow_aproval') {
|
||||||
history.push(`/jobs/${node.job.id}/details`);
|
history.push(`/jobs/${job.id}/details`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -90,7 +91,7 @@ function WorkflowOutputNode({ i18n, mouseEnter, mouseLeave, node }) {
|
|||||||
id={`node-${node.id}`}
|
id={`node-${node.id}`}
|
||||||
transform={`translate(${nodePositions[node.id].x},${nodePositions[node.id]
|
transform={`translate(${nodePositions[node.id].x},${nodePositions[node.id]
|
||||||
.y - nodePositions[1].y})`}
|
.y - nodePositions[1].y})`}
|
||||||
job={node.job}
|
job={job}
|
||||||
onClick={handleNodeClick}
|
onClick={handleNodeClick}
|
||||||
onMouseEnter={mouseEnter}
|
onMouseEnter={mouseEnter}
|
||||||
onMouseLeave={mouseLeave}
|
onMouseLeave={mouseLeave}
|
||||||
@@ -106,14 +107,14 @@ function WorkflowOutputNode({ i18n, mouseEnter, mouseLeave, node }) {
|
|||||||
/>
|
/>
|
||||||
<foreignObject height="58" width="178" x="1" y="1">
|
<foreignObject height="58" width="178" x="1" y="1">
|
||||||
<NodeContents>
|
<NodeContents>
|
||||||
{node.job ? (
|
{job ? (
|
||||||
<>
|
<>
|
||||||
<JobTopLine>
|
<JobTopLine>
|
||||||
{node.job.status && <StatusIcon status={node.job.status} />}
|
{job.status && <StatusIcon status={job.status} />}
|
||||||
<p>{node.job.name || node.unifiedJobTemplate.name}</p>
|
<p>{job.name || node.unifiedJobTemplate.name}</p>
|
||||||
</JobTopLine>
|
</JobTopLine>
|
||||||
{!!node?.job?.elapsed && (
|
{!!job?.elapsed && (
|
||||||
<Elapsed>{secondsToHHMMSS(node.job.elapsed)}</Elapsed>
|
<Elapsed>{secondsToHHMMSS(job.elapsed)}</Elapsed>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@@ -125,7 +126,7 @@ function WorkflowOutputNode({ i18n, mouseEnter, mouseLeave, node }) {
|
|||||||
)}
|
)}
|
||||||
</NodeContents>
|
</NodeContents>
|
||||||
</foreignObject>
|
</foreignObject>
|
||||||
{(node.unifiedJobTemplate || node.job) && (
|
{(node.unifiedJobTemplate || job) && (
|
||||||
<WorkflowNodeTypeLetter node={node} />
|
<WorkflowNodeTypeLetter node={node} />
|
||||||
)}
|
)}
|
||||||
</NodeG>
|
</NodeG>
|
||||||
|
|||||||
@@ -1,5 +1,18 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import useWebsocket from '../../../util/useWebsocket';
|
import useWebsocket from '../../../util/useWebsocket';
|
||||||
|
import { WorkflowJobsAPI } from '../../../api';
|
||||||
|
|
||||||
|
const fetchWorkflowNodes = async (jobId, pageNo = 1, nodes = []) => {
|
||||||
|
const { data } = await WorkflowJobsAPI.readNodes(jobId, {
|
||||||
|
page_size: 200,
|
||||||
|
page: pageNo,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (data.next) {
|
||||||
|
return fetchWorkflowNodes(jobId, pageNo + 1, nodes.concat(data.results));
|
||||||
|
}
|
||||||
|
return nodes.concat(data.results);
|
||||||
|
};
|
||||||
|
|
||||||
export default function useWsWorkflowOutput(workflowJobId, initialNodes) {
|
export default function useWsWorkflowOutput(workflowJobId, initialNodes) {
|
||||||
const [nodes, setNodes] = useState(initialNodes);
|
const [nodes, setNodes] = useState(initialNodes);
|
||||||
@@ -14,20 +27,53 @@ export default function useWsWorkflowOutput(workflowJobId, initialNodes) {
|
|||||||
|
|
||||||
useEffect(
|
useEffect(
|
||||||
function parseWsMessage() {
|
function parseWsMessage() {
|
||||||
if (
|
async function refreshNodeObjects() {
|
||||||
!nodes ||
|
const refreshedNodes = [];
|
||||||
nodes.length === 0 ||
|
const updatedNodeObjects = await fetchWorkflowNodes(workflowJobId);
|
||||||
lastMessage?.workflow_job_id !== workflowJobId
|
const updatedNodeObjectsMap = updatedNodeObjects.reduce((map, node) => {
|
||||||
) {
|
map[node.id] = node;
|
||||||
return;
|
return map;
|
||||||
|
}, {});
|
||||||
|
nodes.forEach(node => {
|
||||||
|
if (node.id === 1) {
|
||||||
|
// This is our artificial start node
|
||||||
|
refreshedNodes.push({
|
||||||
|
...node,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
refreshedNodes.push({
|
||||||
|
...node,
|
||||||
|
originalNodeObject:
|
||||||
|
updatedNodeObjectsMap[node.originalNodeObject.id],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setNodes(refreshedNodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
const index = nodes.findIndex(
|
if (
|
||||||
node => node?.originalNodeObject?.id === lastMessage.workflow_node_id
|
lastMessage?.unified_job_id === workflowJobId &&
|
||||||
);
|
['successful', 'failed', 'error', 'cancelled'].includes(
|
||||||
|
lastMessage.status
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
refreshNodeObjects();
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
!nodes ||
|
||||||
|
nodes.length === 0 ||
|
||||||
|
lastMessage?.workflow_job_id !== workflowJobId
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (index > -1) {
|
const index = nodes.findIndex(
|
||||||
setNodes(updateNode(nodes, index, lastMessage));
|
node => node?.originalNodeObject?.id === lastMessage.workflow_node_id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (index > -1) {
|
||||||
|
setNodes(updateNode(nodes, index, lastMessage));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[lastMessage] // eslint-disable-line react-hooks/exhaustive-deps
|
[lastMessage] // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|||||||
Reference in New Issue
Block a user