Commit 3b5f1d2e authored by Frédéric Caplette's avatar Frédéric Caplette Committed by Olena Horal-Koretska

Fix pipeline throwing error when needs is undefined

parent 59ae7774
...@@ -55,21 +55,25 @@ export const createNodeDict = (nodes) => { ...@@ -55,21 +55,25 @@ export const createNodeDict = (nodes) => {
export const makeLinksFromNodes = (nodes, nodeDict) => { export const makeLinksFromNodes = (nodes, nodeDict) => {
const constantLinkValue = 10; // all links are the same weight const constantLinkValue = 10; // all links are the same weight
return nodes return nodes
.map((group) => { .map(({ jobs, name: groupName }) =>
return group.jobs.map((job) => { jobs.map(({ needs = [] }) =>
if (!job.needs) { needs.reduce((acc, needed) => {
return []; // It's possible that we have an optional job, which
} // is being needed by another job. In that scenario,
// the needed job doesn't exist, so we don't want to
return job.needs.map((needed) => { // create link for it.
return { if (nodeDict[needed]?.name) {
source: nodeDict[needed]?.name, acc.push({
target: group.name, source: nodeDict[needed].name,
target: groupName,
value: constantLinkValue, value: constantLinkValue,
};
}); });
}); }
})
return acc;
}, []),
),
)
.flat(2); .flat(2);
}; };
......
...@@ -39,7 +39,13 @@ export const generateJobNeedsDict = (jobs = {}) => { ...@@ -39,7 +39,13 @@ export const generateJobNeedsDict = (jobs = {}) => {
} }
return jobs[jobName].needs return jobs[jobName].needs
.map((job) => { .reduce((needsAcc, job) => {
// It's possible that a needs refer to an optional job
// that is not defined in which case we don't add that entry
if (!jobs[job]) {
return needsAcc;
}
// If we already have the needs of a job in the accumulator, // If we already have the needs of a job in the accumulator,
// then we use the memoized data instead of the recursive call // then we use the memoized data instead of the recursive call
// to save some performance. // to save some performance.
...@@ -50,11 +56,11 @@ export const generateJobNeedsDict = (jobs = {}) => { ...@@ -50,11 +56,11 @@ export const generateJobNeedsDict = (jobs = {}) => {
// to the list of `needs` to ensure we can properly reference it. // to the list of `needs` to ensure we can properly reference it.
const group = jobs[job]; const group = jobs[job];
if (group.size > 1) { if (group.size > 1) {
return [job, group.name, newNeeds]; return [...needsAcc, job, group.name, newNeeds];
} }
return [job, newNeeds]; return [...needsAcc, job, newNeeds];
}) }, [])
.flat(Infinity); .flat(Infinity);
}; };
......
---
title: Fix pipeline graph undefined needs error
merge_request: 62027
author:
type: fixed
...@@ -398,6 +398,8 @@ export const multiNote = { ...@@ -398,6 +398,8 @@ export const multiNote = {
}, },
}; };
export const missingJob = 'missing_job';
/* /*
It is important that the base include parallel jobs It is important that the base include parallel jobs
as well as non-parallel jobs with spaces in the name to prevent as well as non-parallel jobs with spaces in the name to prevent
...@@ -657,4 +659,16 @@ export const mockParsedGraphQLNodes = [ ...@@ -657,4 +659,16 @@ export const mockParsedGraphQLNodes = [
], ],
__typename: 'CiGroup', __typename: 'CiGroup',
}, },
{
category: 'production',
name: 'production_e',
size: 1,
jobs: [
{
name: 'production_e',
needs: [missingJob],
},
],
__typename: 'CiGroup',
},
]; ];
...@@ -10,7 +10,7 @@ import { ...@@ -10,7 +10,7 @@ import {
getMaxNodes, getMaxNodes,
} from '~/pipelines/components/parsing_utils'; } from '~/pipelines/components/parsing_utils';
import { mockParsedGraphQLNodes } from './components/dag/mock_data'; import { mockParsedGraphQLNodes, missingJob } from './components/dag/mock_data';
import { generateResponse, mockPipelineResponse } from './graph/mock_data'; import { generateResponse, mockPipelineResponse } from './graph/mock_data';
describe('DAG visualization parsing utilities', () => { describe('DAG visualization parsing utilities', () => {
...@@ -24,6 +24,12 @@ describe('DAG visualization parsing utilities', () => { ...@@ -24,6 +24,12 @@ describe('DAG visualization parsing utilities', () => {
expect(unfilteredLinks[0]).toHaveProperty('target', 'test_a'); expect(unfilteredLinks[0]).toHaveProperty('target', 'test_a');
expect(unfilteredLinks[0]).toHaveProperty('value', 10); expect(unfilteredLinks[0]).toHaveProperty('value', 10);
}); });
it('does not generate a link for non-existing jobs', () => {
const sources = unfilteredLinks.map(({ source }) => source);
expect(sources.includes(missingJob)).toBe(false);
});
}); });
describe('filterByAncestors', () => { describe('filterByAncestors', () => {
...@@ -88,7 +94,7 @@ describe('DAG visualization parsing utilities', () => { ...@@ -88,7 +94,7 @@ describe('DAG visualization parsing utilities', () => {
These lengths are determined by the mock data. These lengths are determined by the mock data.
If the data changes, the numbers may also change. If the data changes, the numbers may also change.
*/ */
expect(parsed.nodes).toHaveLength(21); expect(parsed.nodes).toHaveLength(mockParsedGraphQLNodes.length);
expect(cleanedNodes).toHaveLength(12); expect(cleanedNodes).toHaveLength(12);
}); });
}); });
......
...@@ -111,6 +111,28 @@ describe('utils functions', () => { ...@@ -111,6 +111,28 @@ describe('utils functions', () => {
}); });
}); });
it('removes needs which are not in the data', () => {
const inexistantJobName = 'job5';
const jobsWithNeeds = {
[jobName1]: job1,
[jobName2]: job2,
[jobName3]: job3,
[jobName4]: {
name: jobName4,
script: 'echo deploy',
stage: 'deploy',
needs: [inexistantJobName],
},
};
expect(generateJobNeedsDict(jobsWithNeeds)).toEqual({
[jobName1]: [],
[jobName2]: [],
[jobName3]: [jobName1, jobName2],
[jobName4]: [],
});
});
it('handles parallel jobs by adding the group name as a need', () => { it('handles parallel jobs by adding the group name as a need', () => {
const size = 3; const size = 3;
const jobOptimize1 = 'optimize_1'; const jobOptimize1 = 'optimize_1';
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment