Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/ci/pipeline_details/utils/index.js')
-rw-r--r--app/assets/javascripts/ci/pipeline_details/utils/index.js147
1 files changed, 147 insertions, 0 deletions
diff --git a/app/assets/javascripts/ci/pipeline_details/utils/index.js b/app/assets/javascripts/ci/pipeline_details/utils/index.js
new file mode 100644
index 00000000000..9109342707e
--- /dev/null
+++ b/app/assets/javascripts/ci/pipeline_details/utils/index.js
@@ -0,0 +1,147 @@
+import { pickBy } from 'lodash';
+import { parseUrlPathname } from '~/lib/utils/url_utility';
+import {
+ NEEDS_PROPERTY,
+ SUPPORTED_FILTER_PARAMETERS,
+ validPipelineTabNames,
+ pipelineTabName,
+} from '../constants';
+/*
+ The following functions are the main engine in transforming the data as
+ received from the endpoint into the format the d3 graph expects.
+
+ Input is of the form:
+ [nodes]
+ nodes: [{category, name, jobs, size}]
+ category is the stage name
+ name is a group name; in the case that the group has one job, it is
+ also the job name
+ size is the number of parallel jobs
+ jobs: [{ name, needs}]
+ job name is either the same as the group name or group x/y
+ needs: [job-names]
+ needs is an array of job-name strings
+
+ Output is of the form:
+ { nodes: [node], links: [link] }
+ node: { name, category }, + unused info passed through
+ link: { source, target, value }, with source & target being node names
+ and value being a constant
+
+ We create nodes in the GraphQL update function, and then here we create the node dictionary,
+ then create links, and then dedupe the links, so that in the case where
+ job 4 depends on job 1 and job 2, and job 2 depends on job 1, we show only a single link
+ from job 1 to job 2 then another from job 2 to job 4.
+
+ CREATE LINKS
+ nodes.name -> target
+ nodes.name.needs.each -> source (source is the name of the group, not the parallel job)
+ 10 -> value (constant)
+ */
+
+export const createNodeDict = (nodes, { needsKey = NEEDS_PROPERTY } = {}) => {
+ return nodes.reduce((acc, node) => {
+ const newNode = {
+ ...node,
+ needs: node.jobs.map((job) => job[needsKey] || []).flat(),
+ };
+
+ if (node.size > 1) {
+ node.jobs.forEach((job) => {
+ acc[job.name] = newNode;
+ });
+ }
+
+ acc[node.name] = newNode;
+ return acc;
+ }, {});
+};
+
+export const validateParams = (params) => {
+ return pickBy(params, (val, key) => SUPPORTED_FILTER_PARAMETERS.includes(key) && val);
+};
+
+/**
+ * This function takes the stages array and transform it
+ * into a hash where each key is a job name and the job data
+ * is associated to that key.
+ * @param {Array} stages
+ * @returns {Object} - Hash of jobs
+ */
+export const createJobsHash = (stages = []) => {
+ const nodes = stages.flatMap(({ groups }) => groups);
+ return createNodeDict(nodes);
+};
+
+/**
+ * This function takes the jobs hash generated by
+ * `createJobsHash` function and returns an easier
+ * structure to work with for needs relationship
+ * where the key is the job name and the value is an
+ * array of all the needs this job has recursively
+ * (includes the needs of the needs)
+ * @param {Object} jobs
+ * @returns {Object} - Hash of jobs and array of needs
+ */
+export const generateJobNeedsDict = (jobs = {}) => {
+ const arrOfJobNames = Object.keys(jobs);
+
+ return arrOfJobNames.reduce((acc, value) => {
+ const recursiveNeeds = (jobName) => {
+ if (!jobs[jobName]?.needs) {
+ return [];
+ }
+
+ return jobs[jobName].needs
+ .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,
+ // then we use the memoized data instead of the recursive call
+ // to save some performance.
+ const newNeeds = acc[job] ?? recursiveNeeds(job);
+
+ // In case it's a parallel job (size > 1), the name of the group
+ // and the job will be different. This mean we also need to add the group name
+ // to the list of `needs` to ensure we can properly reference it.
+ const group = jobs[job];
+ if (group.size > 1) {
+ return [...needsAcc, job, group.name, newNeeds];
+ }
+
+ return [...needsAcc, job, newNeeds];
+ }, [])
+ .flat(Infinity);
+ };
+
+ // To ensure we don't have duplicates job relationship when 2 jobs
+ // needed by another both depends on the same jobs, we remove any
+ // duplicates from the array.
+ const uniqueValues = Array.from(new Set(recursiveNeeds(value)));
+
+ return { ...acc, [value]: uniqueValues };
+ }, {});
+};
+
+export const getPipelineDefaultTab = (url) => {
+ const strippedUrl = parseUrlPathname(url);
+ const regexp = /\w*$/;
+ const [tabName] = strippedUrl.match(regexp);
+
+ if (tabName && validPipelineTabNames.includes(tabName)) return tabName;
+ if (tabName === '') return pipelineTabName;
+
+ return null;
+};
+
+export const graphqlEtagPipelinePath = (graphqlPath, pipelineId) => {
+ return `${graphqlPath}pipelines/id/${pipelineId}`;
+};
+
+export const graphqlEtagMergeRequestPipelines = (graphqlPath, mergeRequestId) => {
+ return `${graphqlPath}merge_requests/id/${mergeRequestId}`;
+};