{"version":3,"sources":["webpack:///./performance/constants.js","webpack:///./ci/pipeline_details/graph/api_utils.js","webpack:///./ci/pipeline_details/graph/perf_utils.js","webpack:///./ci/pipeline_details/graph/utils.js","webpack:///./ci/pipeline_details/utils/parsing_utils.js","webpack:///./ci/pipeline_details/utils/index.js","webpack:///./ci/pipeline_details/utils/drawing_utils.js","webpack:///./ci/pipeline_details/utils/unwrapping_utils.js","webpack:///./vue_shared/components/ci_icon/ci_icon.vue","webpack:///./vue_shared/components/ci_icon/ci_icon.vue?b66d","webpack:///vue_shared/components/ci_icon/ci_icon.vue","webpack:///./vue_shared/components/ci_icon/ci_icon.vue?9e67","webpack:///./performance/utils.js","webpack:///./ci/pipeline_details/constants.js"],"names":["PERFORMANCE_TYPE_MARK","PERFORMANCE_TYPE_MEASURE","SNIPPET_MARK_VIEW_APP_START","SNIPPET_MARK_EDIT_APP_START","SNIPPET_MARK_BLOBS_CONTENT","SNIPPET_MEASURE_BLOBS_CONTENT","WEBIDE_MARK_APP_START","WEBIDE_MARK_FILE_CLICKED","WEBIDE_MARK_FILE_FINISH","WEBIDE_MARK_REPO_EDITOR_START","WEBIDE_MARK_REPO_EDITOR_FINISH","WEBIDE_MARK_FETCH_BRANCH_DATA_START","WEBIDE_MARK_FETCH_BRANCH_DATA_FINISH","WEBIDE_MARK_FETCH_FILE_DATA_START","WEBIDE_MARK_FETCH_FILE_DATA_FINISH","WEBIDE_MARK_FETCH_FILES_START","WEBIDE_MARK_FETCH_FILES_FINISH","WEBIDE_MARK_FETCH_PROJECT_DATA_START","WEBIDE_MARK_FETCH_PROJECT_DATA_FINISH","WEBIDE_MEASURE_FILE_AFTER_INTERACTION","WEBIDE_MEASURE_FETCH_PROJECT_DATA","WEBIDE_MEASURE_FETCH_BRANCH_DATA","WEBIDE_MEASURE_FETCH_FILE_DATA","WEBIDE_MEASURE_BEFORE_VUE","WEBIDE_MEASURE_REPO_EDITOR","WEBIDE_MEASURE_FETCH_FILES","MR_DIFFS_MARK_FILE_TREE_START","MR_DIFFS_MARK_FILE_TREE_END","MR_DIFFS_MARK_DIFF_FILES_START","MR_DIFFS_MARK_FIRST_DIFF_FILE_SHOWN","MR_DIFFS_MARK_DIFF_FILES_END","MR_DIFFS_MEASURE_FILE_TREE_DONE","MR_DIFFS_MEASURE_DIFF_FILES_DONE","PIPELINES_DETAIL_LINKS_MARK_CALCULATE_START","PIPELINES_DETAIL_LINKS_MARK_CALCULATE_END","PIPELINES_DETAIL_LINKS_MEASURE_CALCULATION","PIPELINES_DETAIL_LINK_DURATION","PIPELINES_DETAIL_LINKS_TOTAL","PIPELINES_DETAIL_LINKS_JOB_RATIO","REPO_BLOB_LOAD_VIEWER_START","REPO_BLOB_SWITCH_TO_VIEWER_START","REPO_BLOB_LOAD_VIEWER_FINISH","REPO_BLOB_LOAD_VIEWER","REPO_BLOB_SWITCH_VIEWER","DESIGN_MARK_APP_START","DESIGN_MEASURE_BEFORE_APP","DESIGN_MAIN_IMAGE_OUTPUT","finishPerfMeasureAndSend","numLinks","numGroups","metricsPath","performanceMarkAndMeasure","mark","measures","name","start","window","requestAnimationFrame","_window$performance$g","duration","performance","getEntriesByName","data","histograms","value","path","stats","axios","post","catch","err","reportToSentry","addMulti","mainPipelineProjectPath","linkedPipeline","multiproject","project","fullPath","calculatePipelineLayersInfo","pipeline","componentName","shouldCollectMetrics","Boolean","layers","listByLayers","linksData","length","getQueryHeaders","etagResource","etagQueryHeaders","serializeGqlErr","gqlError","locations","message","flatMap","loc","Object","entries","flat","join","serializeLoadErrors","errors","graphQLErrors","networkError","_isEmpty","map","transformId","id","getIdFromGraphQLId","unwrapPipelineData","_data$project","upstream","downstream","stages","nodes","updatedStages","lookup","unwrapStagesWithNeedsAndLookup","stagesLookup","bind","validateConfigPaths","_value$graphqlResourc","graphqlResourceEtag","confirmJobConfirmationMessage","jobName","confirmAction","title","sprintf","s__","sanitize","modalHtmlMessage","__","primaryBtnText","deduplicate","item","itemIndex","arr","findIndex","test","source","target","getAllAncestors","nodeDict","needs","node","_nodeDict$node","filter","parseData","needsKey","NEEDS_PROPERTY","createNodeDict","allLinks","jobs","groupName","job","reduce","acc","needed","_nodeDict$needed","push","makeLinksFromNodes","links","targetNodeNeedsMinusSource","need","includes","filterByAncestors","arrayOfJobs","groups","parsedData","explicitParsedData","EXPLICIT_NEEDS_PROPERTY","pipelineLayers","createSankey","layer","generateColumnsFromLayersListMemoized","_memoize","idx","status","action","_stages$stageIdx","stageIdx","groupIdx","keepLatestDownstreamPipelines","downstreamPipelines","_pipeline$sourceJob","_pipeline$source_job","source_job","retried","sourceJob","newNode","size","forEach","validateParams","params","_pickBy","val","key","SUPPORTED_FILTER_PARAMETERS","createJobsHash","generateJobNeedsDict","keys","recursiveNeeds","_jobs$jobName","needsAcc","_acc$job","newNeeds","group","Infinity","uniqueValues","Array","from","Set","getPipelineDefaultTab","url","strippedUrl","parseUrlPathname","tabName","match","validPipelineTabNames","pipelineTabName","graphqlEtagPipelinePath","graphqlPath","pipelineId","graphqlEtagStagePath","stageId","width","height","nodeWidth","nodePadding","paddingForLabels","sankeyGenerator","sankey","nodeId","nodeAlign","sankeyLeft","extent","d","generateLinksData","containerID","modifier","containerEl","document","getElementById","link","d3","sourceId","targetId","modifiedSourceId","modifiedTargetId","sourceNodeEl","targetNodeEl","sourceNodeCoordinates","getBoundingClientRect","targetNodeCoordinates","containerCoordinates","paddingLeft","parseFloat","getComputedStyle","getPropertyValue","paddingTop","sourceNodeX","right","x","sourceNodeY","top","y","targetNodeX","targetNodeY","sourceNodeLeftX","left","firstPointCoordinateX","moveTo","straightLineDestinationX","controlPointX","lineTo","bezierCurveTo","ref","stageName","toString","unwrapNodesWithName","jobArray","prop","field","denodedStages","unwrappedNestedGroups","stage","groupsWithStageName","lookupMap","groupsWithJobs","denodedJobArray","explicitNeedsUnwrapped","unwrapJobWithNeeds","unwrapStagesWithNeeds","components","GlIcon","directives","GlTooltip","GlTooltipDirective","props","type","required","validator","icon","startsWith","showStatusText","default","showTooltip","useLink","computed","componentType","href","_this$status","text","ariaLabel","_this$status2","detailsPath","details_path","variant","component","_vm","this","_c","_self","rawName","modifiers","tag","staticClass","class","attrs","on","$event","$emit","_v","_s","_e","measure","end","CANCEL_REQUEST","testStatus","FAILED","SKIPPED","SUCCESS","ERROR","UNKNOWN","DEFAULT","DELETE_FAILURE","DRAW_FAILURE","LOAD_FAILURE","POST_FAILURE","jobsTabName","failedJobsTabName","testReportTabName","manualVariablesTabName","securityTabName","licensesTabName","codeQualityTabName","TOAST_MESSAGE","DEFAULT_FIELDS","label","columnClass","tdClass"],"mappings":"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","file":"28.39bf1362.chunk.js","sourcesContent":["export const PERFORMANCE_TYPE_MARK = 'mark';\nexport const PERFORMANCE_TYPE_MEASURE = 'measure';\n\n//\n// SNIPPET namespace\n//\n\n// Marks\nexport const SNIPPET_MARK_VIEW_APP_START = 'snippet-view-app-start';\nexport const SNIPPET_MARK_EDIT_APP_START = 'snippet-edit-app-start';\nexport const SNIPPET_MARK_BLOBS_CONTENT = 'snippet-blobs-content-finished';\n\n// Measures\nexport const SNIPPET_MEASURE_BLOBS_CONTENT = 'snippet-blobs-content';\n\n//\n// WebIDE namespace\n//\n\n// Marks\nexport const WEBIDE_MARK_APP_START = 'webide-app-start';\nexport const WEBIDE_MARK_FILE_CLICKED = 'webide-file-clicked';\nexport const WEBIDE_MARK_FILE_FINISH = 'webide-file-finished';\nexport const WEBIDE_MARK_REPO_EDITOR_START = 'webide-init-editor-start';\nexport const WEBIDE_MARK_REPO_EDITOR_FINISH = 'webide-init-editor-finish';\nexport const WEBIDE_MARK_FETCH_BRANCH_DATA_START = 'webide-getBranchData-start';\nexport const WEBIDE_MARK_FETCH_BRANCH_DATA_FINISH = 'webide-getBranchData-finish';\nexport const WEBIDE_MARK_FETCH_FILE_DATA_START = 'webide-getFileData-start';\nexport const WEBIDE_MARK_FETCH_FILE_DATA_FINISH = 'webide-getFileData-finish';\nexport const WEBIDE_MARK_FETCH_FILES_START = 'webide-getFiles-start';\nexport const WEBIDE_MARK_FETCH_FILES_FINISH = 'webide-getFiles-finish';\nexport const WEBIDE_MARK_FETCH_PROJECT_DATA_START = 'webide-getProjectData-start';\nexport const WEBIDE_MARK_FETCH_PROJECT_DATA_FINISH = 'webide-getProjectData-finish';\n\n// Measures\nexport const WEBIDE_MEASURE_FILE_AFTER_INTERACTION = 'webide-file-loading-after-interaction';\nexport const WEBIDE_MEASURE_FETCH_PROJECT_DATA = 'WebIDE: Project data';\nexport const WEBIDE_MEASURE_FETCH_BRANCH_DATA = 'WebIDE: Branch data';\nexport const WEBIDE_MEASURE_FETCH_FILE_DATA = 'WebIDE: File data';\nexport const WEBIDE_MEASURE_BEFORE_VUE = 'WebIDE: Before Vue app';\nexport const WEBIDE_MEASURE_REPO_EDITOR = 'WebIDE: Repo Editor';\nexport const WEBIDE_MEASURE_FETCH_FILES = 'WebIDE: Fetch Files';\n\n//\n// MR Diffs namespace\n//\n\n// Marks\nexport const MR_DIFFS_MARK_FILE_TREE_START = 'mr-diffs-mark-file-tree-start';\nexport const MR_DIFFS_MARK_FILE_TREE_END = 'mr-diffs-mark-file-tree-end';\nexport const MR_DIFFS_MARK_DIFF_FILES_START = 'mr-diffs-mark-diff-files-start';\nexport const MR_DIFFS_MARK_FIRST_DIFF_FILE_SHOWN = 'mr-diffs-mark-first-diff-file-shown';\nexport const MR_DIFFS_MARK_DIFF_FILES_END = 'mr-diffs-mark-diff-files-end';\n\n// Measures\nexport const MR_DIFFS_MEASURE_FILE_TREE_DONE = 'mr-diffs-measure-file-tree-done';\nexport const MR_DIFFS_MEASURE_DIFF_FILES_DONE = 'mr-diffs-measure-diff-files-done';\n\n//\n// Pipelines Detail namespace\n//\n\n// Marks\nexport const PIPELINES_DETAIL_LINKS_MARK_CALCULATE_START =\n 'pipelines-detail-links-mark-calculate-start';\nexport const PIPELINES_DETAIL_LINKS_MARK_CALCULATE_END =\n 'pipelines-detail-links-mark-calculate-end';\n\n// Measures\nexport const PIPELINES_DETAIL_LINKS_MEASURE_CALCULATION =\n 'Pipelines Detail Graph: Links Calculation';\n\n// Metrics\n// Note: These strings must match the backend\n// (defined in: app/services/ci/prometheus_metrics/observe_histograms_service.rb)\nexport const PIPELINES_DETAIL_LINK_DURATION = 'pipeline_graph_link_calculation_duration_seconds';\nexport const PIPELINES_DETAIL_LINKS_TOTAL = 'pipeline_graph_links_total';\nexport const PIPELINES_DETAIL_LINKS_JOB_RATIO = 'pipeline_graph_links_per_job_ratio';\n\n//\n// REPO BROWSER NAMESPACE\n//\n\n// Marks\nexport const REPO_BLOB_LOAD_VIEWER_START = 'blobviewer-load-viewer-start';\nexport const REPO_BLOB_SWITCH_TO_VIEWER_START = 'blobviewer-switch-to-viewerr-start';\nexport const REPO_BLOB_LOAD_VIEWER_FINISH = 'blobviewer-load-viewer-finish';\n\n// Measures\nexport const REPO_BLOB_LOAD_VIEWER = 'Repository File Viewer: loading the viewer';\nexport const REPO_BLOB_SWITCH_VIEWER = 'Repository File Viewer: switching the viewer';\n\n//\n// DESIGN MANAGEMENT NAMESPACE\n//\n\n// Marks\nexport const DESIGN_MARK_APP_START = 'design-app-start';\n\n// Measures\nexport const DESIGN_MEASURE_BEFORE_APP = 'Design Management: Before the Vue app';\nexport const DESIGN_MAIN_IMAGE_OUTPUT = 'Design Management: Single image preview';\n","import axios from '~/lib/utils/axios_utils';\nimport { reportToSentry } from '~/ci/utils';\n\nexport const reportPerformance = (path, stats) => {\n // FIXME: https://gitlab.com/gitlab-org/gitlab/-/issues/330245\n if (!path) {\n return;\n }\n\n axios.post(path, stats).catch((err) => {\n reportToSentry('links_inner_perf', `error: ${err}`);\n });\n};\n","import {\n PIPELINES_DETAIL_LINKS_MARK_CALCULATE_START,\n PIPELINES_DETAIL_LINKS_MARK_CALCULATE_END,\n PIPELINES_DETAIL_LINKS_MEASURE_CALCULATION,\n PIPELINES_DETAIL_LINK_DURATION,\n PIPELINES_DETAIL_LINKS_TOTAL,\n PIPELINES_DETAIL_LINKS_JOB_RATIO,\n} from '~/performance/constants';\n\nimport { performanceMarkAndMeasure } from '~/performance/utils';\nimport { reportPerformance } from './api_utils';\n\nexport const beginPerfMeasure = () => {\n performanceMarkAndMeasure({ mark: PIPELINES_DETAIL_LINKS_MARK_CALCULATE_START });\n};\n\nexport const finishPerfMeasureAndSend = (numLinks, numGroups, metricsPath) => {\n performanceMarkAndMeasure({\n mark: PIPELINES_DETAIL_LINKS_MARK_CALCULATE_END,\n measures: [\n {\n name: PIPELINES_DETAIL_LINKS_MEASURE_CALCULATION,\n start: PIPELINES_DETAIL_LINKS_MARK_CALCULATE_START,\n },\n ],\n });\n\n window.requestAnimationFrame(() => {\n const duration = window.performance.getEntriesByName(\n PIPELINES_DETAIL_LINKS_MEASURE_CALCULATION,\n )[0]?.duration;\n\n if (!duration) {\n return;\n }\n\n const data = {\n histograms: [\n { name: PIPELINES_DETAIL_LINK_DURATION, value: duration / 1000 },\n { name: PIPELINES_DETAIL_LINKS_TOTAL, value: numLinks },\n {\n name: PIPELINES_DETAIL_LINKS_JOB_RATIO,\n value: numLinks / numGroups,\n },\n ],\n };\n\n reportPerformance(metricsPath, data);\n });\n};\n","import { isEmpty } from 'lodash';\nimport { getIdFromGraphQLId, etagQueryHeaders } from '~/graphql_shared/utils';\nimport { reportToSentry } from '~/ci/utils';\n\nimport { listByLayers } from '~/ci/pipeline_details/utils/parsing_utils';\nimport { unwrapStagesWithNeedsAndLookup } from '~/ci/pipeline_details/utils/unwrapping_utils';\nimport { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';\nimport { sanitize } from '~/lib/dompurify';\nimport { __, s__, sprintf } from '~/locale';\nimport { beginPerfMeasure, finishPerfMeasureAndSend } from './perf_utils';\n\nexport { toggleQueryPollingByVisibility } from '~/graphql_shared/utils';\n\nconst addMulti = (mainPipelineProjectPath, linkedPipeline) => {\n return {\n ...linkedPipeline,\n multiproject: mainPipelineProjectPath !== linkedPipeline.project.fullPath,\n };\n};\n\nconst calculatePipelineLayersInfo = (pipeline, componentName, metricsPath) => {\n const shouldCollectMetrics = Boolean(metricsPath);\n\n if (shouldCollectMetrics) {\n beginPerfMeasure();\n }\n\n let layers = null;\n\n try {\n layers = listByLayers(pipeline);\n\n if (shouldCollectMetrics) {\n finishPerfMeasureAndSend(layers.linksData.length, layers.numGroups, metricsPath);\n }\n } catch (err) {\n reportToSentry(componentName, err);\n }\n\n return layers;\n};\n\nconst getQueryHeaders = (etagResource) =>\n etagQueryHeaders('verify/ci/pipeline-graph', etagResource);\n\nconst serializeGqlErr = (gqlError) => {\n const { locations = [], message = '', path = [] } = gqlError;\n\n // eslint-disable-next-line @gitlab/require-i18n-strings\n return `\n ${message}.\n Locations: ${locations\n .flatMap((loc) => Object.entries(loc))\n .flat(2)\n .join(' ')}.\n Path: ${path.join(', ')}.\n `;\n};\n\nconst serializeLoadErrors = (errors) => {\n const { gqlError, graphQLErrors, networkError, message } = errors;\n\n if (!isEmpty(graphQLErrors)) {\n return graphQLErrors.map((err) => serializeGqlErr(err)).join('; ');\n }\n\n if (!isEmpty(gqlError)) {\n return serializeGqlErr(gqlError);\n }\n\n if (!isEmpty(networkError)) {\n return `Network error: ${networkError.message}`; // eslint-disable-line @gitlab/require-i18n-strings\n }\n\n return message;\n};\n\nconst transformId = (linkedPipeline) => {\n return { ...linkedPipeline, id: getIdFromGraphQLId(linkedPipeline.id) };\n};\n\nconst unwrapPipelineData = (mainPipelineProjectPath, data) => {\n if (!data?.project?.pipeline) {\n return null;\n }\n\n const { pipeline } = data.project;\n\n const {\n upstream,\n downstream,\n stages: { nodes: stages },\n } = pipeline;\n\n const { stages: updatedStages, lookup } = unwrapStagesWithNeedsAndLookup(stages);\n\n return {\n ...pipeline,\n id: getIdFromGraphQLId(pipeline.id),\n stages: updatedStages,\n stagesLookup: lookup,\n upstream: upstream\n ? [upstream].map(addMulti.bind(null, mainPipelineProjectPath)).map(transformId)\n : [],\n downstream: downstream\n ? downstream.nodes.map(addMulti.bind(null, mainPipelineProjectPath)).map(transformId)\n : [],\n };\n};\n\nconst validateConfigPaths = (value) => value.graphqlResourceEtag?.length > 0;\n\nconst confirmJobConfirmationMessage = (jobName, message) => {\n return confirmAction(null, {\n title: sprintf(s__('PipelineGraph|Are you sure you want to run %{jobName}?'), {\n jobName: sanitize(jobName),\n }),\n modalHtmlMessage: `\n <p>${sprintf(__('Custom confirmation message: %{message}'), {\n message: sanitize(message),\n })}</p>\n <p>${s__('PipelineGraph|Do you want to continue?')}</p>\n `,\n primaryBtnText: sprintf(__('Yes, run %{jobName}'), {\n jobName: sanitize(jobName),\n }),\n });\n};\n\nexport {\n calculatePipelineLayersInfo,\n getQueryHeaders,\n serializeGqlErr,\n serializeLoadErrors,\n unwrapPipelineData,\n validateConfigPaths,\n confirmJobConfirmationMessage,\n};\n","import { memoize } from 'lodash';\nimport { EXPLICIT_NEEDS_PROPERTY, NEEDS_PROPERTY } from '../constants';\nimport { createSankey } from './drawing_utils';\nimport { createNodeDict } from './index';\n\n/*\n A peformant alternative to lodash's isEqual. Because findIndex always finds\n the first instance of a match, if the found index is not the first, we know\n it is in fact a duplicate.\n*/\nconst deduplicate = (item, itemIndex, arr) => {\n const foundIdx = arr.findIndex((test) => {\n return test.source === item.source && test.target === item.target;\n });\n\n return foundIdx === itemIndex;\n};\n\nexport const makeLinksFromNodes = (nodes, nodeDict, { needsKey = NEEDS_PROPERTY } = {}) => {\n const constantLinkValue = 10; // all links are the same weight\n return nodes\n .map(({ jobs, name: groupName }) =>\n jobs.map((job) => {\n const needs = job[needsKey] || [];\n\n return needs.reduce((acc, needed) => {\n // It's possible that we have an optional job, which\n // is being needed by another job. In that scenario,\n // the needed job doesn't exist, so we don't want to\n // create link for it.\n if (nodeDict[needed]?.name) {\n acc.push({\n source: nodeDict[needed].name,\n target: groupName,\n value: constantLinkValue,\n });\n }\n\n return acc;\n }, []);\n }),\n )\n .flat(2);\n};\n\nexport const getAllAncestors = (nodes, nodeDict) => {\n const needs = nodes\n .map((node) => {\n return nodeDict[node]?.needs || '';\n })\n .flat()\n .filter(Boolean)\n .filter(deduplicate);\n\n if (needs.length) {\n return [...needs, ...getAllAncestors(needs, nodeDict)];\n }\n\n return [];\n};\n\nexport const filterByAncestors = (links, nodeDict) =>\n links.filter(({ target, source }) => {\n /*\n\n for every link, check out it's target\n for every target, get the target node's needs\n then drop the current link source from that list\n\n call a function to get all ancestors, recursively\n is the current link's source in the list of all parents?\n then we drop this link\n\n */\n const targetNode = target;\n const targetNodeNeeds = nodeDict[targetNode].needs;\n const targetNodeNeedsMinusSource = targetNodeNeeds.filter((need) => need !== source);\n const allAncestors = getAllAncestors(targetNodeNeedsMinusSource, nodeDict);\n return !allAncestors.includes(source);\n });\n\nexport const parseData = (nodes, { needsKey = NEEDS_PROPERTY } = {}) => {\n const nodeDict = createNodeDict(nodes, { needsKey });\n const allLinks = makeLinksFromNodes(nodes, nodeDict, { needsKey });\n const filteredLinks = allLinks.filter(deduplicate);\n const links = filterByAncestors(filteredLinks, nodeDict);\n\n return { nodes, links };\n};\n\n/*\n The number of nodes in the most populous generation drives the height of the graph.\n*/\n\nexport const getMaxNodes = (nodes) => {\n const counts = nodes.reduce((acc, { layer }) => {\n if (!acc[layer]) {\n acc[layer] = 0;\n }\n\n acc[layer] += 1;\n\n return acc;\n }, []);\n\n return Math.max(...counts);\n};\n\n/*\n This utility accepts unwrapped pipeline data in the format returned from\n our standard pipeline GraphQL query and returns a list of names by layer\n for the layer view. It can be combined with the stageLookup on the pipeline\n to generate columns by layer.\n*/\n\nexport const listByLayers = ({ stages }) => {\n const arrayOfJobs = stages.flatMap(({ groups }) => groups);\n const parsedData = parseData(arrayOfJobs);\n const explicitParsedData = parseData(arrayOfJobs, { needsKey: EXPLICIT_NEEDS_PROPERTY });\n const dataWithLayers = createSankey()(explicitParsedData);\n\n const pipelineLayers = dataWithLayers.nodes.reduce((acc, { layer, name }) => {\n /* sort groups by layer */\n\n if (!acc[layer]) {\n acc[layer] = [];\n }\n\n acc[layer].push(name);\n\n return acc;\n }, []);\n\n return {\n linksData: parsedData.links,\n numGroups: arrayOfJobs.length,\n pipelineLayers,\n };\n};\n\nexport const generateColumnsFromLayersListBare = ({ stages, stagesLookup }, pipelineLayers) => {\n return pipelineLayers.map((layers, idx) => {\n /*\n Look up the groups in each layer,\n then add each set of layer groups to a stage-like object.\n */\n\n const groups = layers.map((id) => {\n const { stageIdx, groupIdx } = stagesLookup[id];\n return stages[stageIdx]?.groups?.[groupIdx];\n });\n\n return {\n name: '',\n id: `layer-${idx}`,\n status: { action: null },\n groups: groups.filter(Boolean),\n };\n });\n};\n\nexport const generateColumnsFromLayersListMemoized = memoize(generateColumnsFromLayersListBare);\n\nexport const keepLatestDownstreamPipelines = (downstreamPipelines = []) => {\n return downstreamPipelines.filter((pipeline) => {\n if (pipeline.source_job) {\n return !pipeline?.source_job?.retried || false;\n }\n\n return !pipeline?.sourceJob?.retried || false;\n });\n};\n","import { pickBy } from 'lodash';\nimport { parseUrlPathname } from '~/lib/utils/url_utility';\nimport {\n NEEDS_PROPERTY,\n SUPPORTED_FILTER_PARAMETERS,\n validPipelineTabNames,\n pipelineTabName,\n} from '../constants';\n/*\n The following functions are the main engine in transforming the data as\n received from the endpoint into the format the d3 graph expects.\n\n Input is of the form:\n [nodes]\n nodes: [{category, name, jobs, size}]\n category is the stage name\n name is a group name; in the case that the group has one job, it is\n also the job name\n size is the number of parallel jobs\n jobs: [{ name, needs}]\n job name is either the same as the group name or group x/y\n needs: [job-names]\n needs is an array of job-name strings\n\n Output is of the form:\n { nodes: [node], links: [link] }\n node: { name, category }, + unused info passed through\n link: { source, target, value }, with source & target being node names\n and value being a constant\n\n We create nodes in the GraphQL update function, and then here we create the node dictionary,\n then create links, and then dedupe the links, so that in the case where\n job 4 depends on job 1 and job 2, and job 2 depends on job 1, we show only a single link\n from job 1 to job 2 then another from job 2 to job 4.\n\n CREATE LINKS\n nodes.name -> target\n nodes.name.needs.each -> source (source is the name of the group, not the parallel job)\n 10 -> value (constant)\n */\n\nexport const createNodeDict = (nodes, { needsKey = NEEDS_PROPERTY } = {}) => {\n return nodes.reduce((acc, node) => {\n const newNode = {\n ...node,\n needs: node.jobs.map((job) => job[needsKey] || []).flat(),\n };\n\n if (node.size > 1) {\n node.jobs.forEach((job) => {\n acc[job.name] = newNode;\n });\n }\n\n acc[node.name] = newNode;\n return acc;\n }, {});\n};\n\nexport const validateParams = (params) => {\n return pickBy(params, (val, key) => SUPPORTED_FILTER_PARAMETERS.includes(key) && val);\n};\n\n/**\n * This function takes the stages array and transform it\n * into a hash where each key is a job name and the job data\n * is associated to that key.\n * @param {Array} stages\n * @returns {Object} - Hash of jobs\n */\nexport const createJobsHash = (stages = []) => {\n const nodes = stages.flatMap(({ groups }) => groups);\n return createNodeDict(nodes);\n};\n\n/**\n * This function takes the jobs hash generated by\n * `createJobsHash` function and returns an easier\n * structure to work with for needs relationship\n * where the key is the job name and the value is an\n * array of all the needs this job has recursively\n * (includes the needs of the needs)\n * @param {Object} jobs\n * @returns {Object} - Hash of jobs and array of needs\n */\nexport const generateJobNeedsDict = (jobs = {}) => {\n const arrOfJobNames = Object.keys(jobs);\n\n return arrOfJobNames.reduce((acc, value) => {\n const recursiveNeeds = (jobName) => {\n if (!jobs[jobName]?.needs) {\n return [];\n }\n\n return jobs[jobName].needs\n .reduce((needsAcc, job) => {\n // It's possible that a needs refer to an optional job\n // that is not defined in which case we don't add that entry\n if (!jobs[job]) {\n return needsAcc;\n }\n\n // If we already have the needs of a job in the accumulator,\n // then we use the memoized data instead of the recursive call\n // to save some performance.\n const newNeeds = acc[job] ?? recursiveNeeds(job);\n\n // In case it's a parallel job (size > 1), the name of the group\n // and the job will be different. This mean we also need to add the group name\n // to the list of `needs` to ensure we can properly reference it.\n const group = jobs[job];\n if (group.size > 1) {\n return [...needsAcc, job, group.name, newNeeds];\n }\n\n return [...needsAcc, job, newNeeds];\n }, [])\n .flat(Infinity);\n };\n\n // To ensure we don't have duplicates job relationship when 2 jobs\n // needed by another both depends on the same jobs, we remove any\n // duplicates from the array.\n const uniqueValues = Array.from(new Set(recursiveNeeds(value)));\n\n return { ...acc, [value]: uniqueValues };\n }, {});\n};\n\nexport const getPipelineDefaultTab = (url) => {\n const strippedUrl = parseUrlPathname(url);\n const regexp = /\\w*$/;\n const [tabName] = strippedUrl.match(regexp);\n\n if (tabName && validPipelineTabNames.includes(tabName)) return tabName;\n if (tabName === '') return pipelineTabName;\n\n return null;\n};\n\nexport const graphqlEtagPipelinePath = (graphqlPath, pipelineId) => {\n return `${graphqlPath}pipelines/id/${pipelineId}`;\n};\n\nexport const graphqlEtagMergeRequestPipelines = (graphqlPath, mergeRequestId) => {\n return `${graphqlPath}merge_requests/id/${mergeRequestId}`;\n};\n\nexport const graphqlEtagStagePath = (graphqlPath, stageId) => {\n return `${graphqlPath}/stages/id/${stageId}`;\n};\n","import * as d3 from 'd3';\nimport { sankey, sankeyLeft } from 'd3-sankey';\n\n/*\n createSankey calls the d3 layout to generate the relationships and positioning\n values for the nodes and links in the graph.\n */\n\nexport const createSankey = ({\n width = 10,\n height = 10,\n nodeWidth = 10,\n nodePadding = 10,\n paddingForLabels = 1,\n} = {}) => {\n const sankeyGenerator = sankey()\n .nodeId(({ name }) => name)\n .nodeAlign(sankeyLeft)\n .nodeWidth(nodeWidth)\n .nodePadding(nodePadding)\n .extent([\n [paddingForLabels, paddingForLabels],\n [width - paddingForLabels, height - paddingForLabels],\n ]);\n return ({ nodes, links }) =>\n sankeyGenerator({\n nodes: nodes.map((d) => ({ ...d })),\n links: links.map((d) => ({ ...d })),\n });\n};\n\nexport const createUniqueLinkId = (stageName, jobName) => `${stageName}-${jobName}`;\n\n/**\n * This function expects its first argument data structure\n * to be the same shaped as the one generated by `parseData`,\n * which contains nodes and links. For each link,\n * we find the nodes in the graph, calculate their coordinates and\n * trace the lines that represent the needs of each job.\n * @param {Object} nodeDict - Resulting object of `parseData` with nodes and links\n * @param {String} containerID - Id for the svg the links will be draw in\n * @returns {Array} Links that contain all the information about them\n */\n\nexport const generateLinksData = (links, containerID, modifier = '') => {\n const containerEl = document.getElementById(containerID);\n\n return links.map((link) => {\n const path = d3.path();\n\n const sourceId = link.source;\n const targetId = link.target;\n\n const modifiedSourceId = `${sourceId}${modifier}`;\n const modifiedTargetId = `${targetId}${modifier}`;\n\n const sourceNodeEl = document.getElementById(modifiedSourceId);\n const targetNodeEl = document.getElementById(modifiedTargetId);\n\n const sourceNodeCoordinates = sourceNodeEl.getBoundingClientRect();\n const targetNodeCoordinates = targetNodeEl.getBoundingClientRect();\n const containerCoordinates = containerEl.getBoundingClientRect();\n\n // Because we add the svg dynamically and calculate the coordinates\n // with plain JS and not D3, we need to account for the fact that\n // the coordinates we are getting are absolutes, but we want to draw\n // relative to the svg container, which starts at `containerCoordinates(x,y)`\n // so we substract these from the total. We also need to remove the padding\n // from the total to make sure it's aligned properly. We then make the line\n // positioned in the center of the job node by adding half the height\n // of the job pill.\n const paddingLeft = parseFloat(\n window.getComputedStyle(containerEl, null).getPropertyValue('padding-left') || 0,\n );\n const paddingTop = parseFloat(\n window.getComputedStyle(containerEl, null).getPropertyValue('padding-top') || 0,\n );\n\n const sourceNodeX = sourceNodeCoordinates.right - containerCoordinates.x - paddingLeft;\n const sourceNodeY =\n sourceNodeCoordinates.top -\n containerCoordinates.y -\n paddingTop +\n sourceNodeCoordinates.height / 2;\n const targetNodeX = targetNodeCoordinates.x - containerCoordinates.x - paddingLeft;\n const targetNodeY =\n targetNodeCoordinates.y -\n containerCoordinates.y -\n paddingTop +\n sourceNodeCoordinates.height / 2;\n\n const sourceNodeLeftX = sourceNodeCoordinates.left - containerCoordinates.x - paddingLeft;\n\n // If the source and target X values are the same,\n // it means the nodes are in the same column so we\n // want to start the line on the left of the pill\n // instead of the right to have a nice curve.\n const firstPointCoordinateX = sourceNodeLeftX === targetNodeX ? sourceNodeLeftX : sourceNodeX;\n\n // First point\n path.moveTo(firstPointCoordinateX, sourceNodeY);\n\n // Make cross-stages lines a straight line all the way\n // until we can safely draw the bezier to look nice.\n // The adjustment number here is a magic number to make things\n // look nice and should change if the padding changes. This goes well\n // with gl-px-9 which we translate with 100px here.\n const straightLineDestinationX = targetNodeX - 100;\n const controlPointX = straightLineDestinationX + (targetNodeX - straightLineDestinationX) / 2;\n\n if (straightLineDestinationX > firstPointCoordinateX) {\n path.lineTo(straightLineDestinationX, sourceNodeY);\n }\n\n // Add bezier curve. The first 4 coordinates are the 2 control\n // points to create the curve, and the last one is the end point (x, y).\n // We want our control points to be in the middle of the line\n path.bezierCurveTo(\n controlPointX,\n sourceNodeY,\n controlPointX,\n targetNodeY,\n targetNodeX,\n targetNodeY,\n );\n\n return {\n ...link,\n source: sourceId,\n target: targetId,\n ref: createUniqueLinkId(sourceId, targetId),\n path: path.toString(),\n };\n });\n};\n","import { reportToSentry } from '~/ci/utils';\nimport { EXPLICIT_NEEDS_PROPERTY, NEEDS_PROPERTY } from '../constants';\n\nconst unwrapGroups = (stages) => {\n return stages.map((stage, idx) => {\n const {\n groups: { nodes: groups },\n } = stage;\n\n /*\n Being peformance conscious here means we don't want to spread and copy the\n group value just to add one parameter.\n */\n /* eslint-disable no-param-reassign */\n const groupsWithStageName = groups.map((group) => {\n group.stageName = stage.name;\n return group;\n });\n /* eslint-enable no-param-reassign */\n\n return { node: { ...stage, groups: groupsWithStageName }, lookup: { stageIdx: idx } };\n });\n};\n\nconst unwrapNodesWithName = (jobArray, prop, field = 'name') => {\n if (jobArray.length < 1) {\n reportToSentry('unwrapping_utils', 'undefined_job_hunt, array empty from backend');\n }\n\n return jobArray.map((job) => {\n if (job[prop]) {\n return { ...job, [prop]: job[prop].nodes.map((item) => item[field] || '') };\n }\n return job;\n });\n};\n\nconst unwrapJobWithNeeds = (denodedJobArray) => {\n const explicitNeedsUnwrapped = unwrapNodesWithName(denodedJobArray, EXPLICIT_NEEDS_PROPERTY);\n return unwrapNodesWithName(explicitNeedsUnwrapped, NEEDS_PROPERTY);\n};\n\nconst unwrapStagesWithNeedsAndLookup = (denodedStages) => {\n const unwrappedNestedGroups = unwrapGroups(denodedStages);\n\n const lookupMap = {};\n\n const nodes = unwrappedNestedGroups.map(({ node, lookup }) => {\n const { groups } = node;\n const groupsWithJobs = groups.map((group, idx) => {\n const jobs = unwrapJobWithNeeds(group.jobs.nodes);\n\n lookupMap[group.name] = { ...lookup, groupIdx: idx };\n return { ...group, jobs };\n });\n\n return { ...node, groups: groupsWithJobs };\n });\n\n return { stages: nodes, lookup: lookupMap };\n};\n\nconst unwrapStagesWithNeeds = (denodedStages) => {\n return unwrapStagesWithNeedsAndLookup(denodedStages).stages;\n};\n\nexport {\n unwrapGroups,\n unwrapJobWithNeeds,\n unwrapNodesWithName,\n unwrapStagesWithNeeds,\n unwrapStagesWithNeedsAndLookup,\n};\n","var render = function render(){var _vm=this,_c=_vm._self._c;return _c(_vm.componentType,{directives:[{name:\"gl-tooltip\",rawName:\"v-gl-tooltip.viewport.left\",modifiers:{\"viewport\":true,\"left\":true}}],tag:\"component\",staticClass:\"ci-icon gl-inline-flex gl-items-center gl-text-sm\",class:`ci-icon-variant-${_vm.variant}`,attrs:{\"variant\":_vm.variant,\"title\":_vm.title,\"aria-label\":_vm.ariaLabel,\"href\":_vm.href,\"data-testid\":\"ci-icon\"},on:{\"click\":function($event){return _vm.$emit('ciStatusBadgeClick')}}},[_c('span',{staticClass:\"ci-icon-gl-icon-wrapper\"},[_c('gl-icon',{attrs:{\"name\":_vm.icon}})],1),(_vm.showStatusText)?_c('span',{staticClass:\"gl-ml-2 gl-mr-3 gl-self-center gl-whitespace-nowrap gl-leading-1\",attrs:{\"data-testid\":\"ci-icon-text\"}},[_vm._v(_vm._s(_vm.status.text))]):_vm._e()])\n}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","import mod from \"-!../../../../../../node_modules/thread-loader/dist/cjs.js??ref--13-0!../../../../../../node_modules/babel-loader/lib/index.js??ref--13-1!../../../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./ci_icon.vue?vue&type=script&lang=js\"; export default mod; export * from \"-!../../../../../../node_modules/thread-loader/dist/cjs.js??ref--13-0!../../../../../../node_modules/babel-loader/lib/index.js??ref--13-1!../../../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./ci_icon.vue?vue&type=script&lang=js\"","<script>\nimport { GlTooltipDirective, GlIcon } from '@gitlab/ui';\nimport { sprintf, __ } from '~/locale';\n\n/**\n * Renders CI icon based on API response shared between all places where it is used.\n *\n * Receives status object containing:\n * status: {\n * icon: \"status_running\" // used to render the icon and CSS class\n * text: \"Running\",\n * detailsPath: '/project1/jobs/1' // can also be details_path\n * }\n *\n * You may use ~/graphql_shared/fragments/ci_icon.fragment.graphql to fetch this\n * from the GraphQL API.\n *\n */\n\nexport default {\n components: {\n GlIcon,\n },\n directives: {\n GlTooltip: GlTooltipDirective,\n },\n props: {\n status: {\n type: Object,\n required: true,\n validator(status) {\n const { icon } = status;\n return typeof icon === 'string' && icon.startsWith('status');\n },\n },\n showStatusText: {\n type: Boolean,\n required: false,\n default: false,\n },\n showTooltip: {\n type: Boolean,\n required: false,\n default: true,\n },\n useLink: {\n type: Boolean,\n default: true,\n required: false,\n },\n },\n computed: {\n componentType() {\n return this.href ? 'a' : 'span';\n },\n title() {\n if (this.showTooltip) {\n // show tooltip only when not showing text already\n return !this.showStatusText ? this.status?.text : null;\n }\n return null;\n },\n ariaLabel() {\n return sprintf(__('Status: %{status}'), { status: this.status?.text });\n },\n href() {\n // href can come from GraphQL (camelCase) or REST API (snake_case)\n if (this.useLink) {\n return this.status.detailsPath || this.status.details_path;\n }\n return null;\n },\n icon() {\n if (this.status.icon) {\n return `${this.status.icon}_borderless`;\n }\n return null;\n },\n variant() {\n switch (this.status.icon) {\n case 'status_success':\n return 'success';\n case 'status_warning':\n case 'status_pending':\n return 'warning';\n case 'status_failed':\n return 'danger';\n case 'status_running':\n return 'info';\n // default covers the styles for the remainder of CI\n // statuses that are not explicitly stated here\n default:\n return 'neutral';\n }\n },\n },\n};\n</script>\n<template>\n <component\n :is=\"componentType\"\n v-gl-tooltip.viewport.left\n class=\"ci-icon gl-inline-flex gl-items-center gl-text-sm\"\n :class=\"`ci-icon-variant-${variant}`\"\n :variant=\"variant\"\n :title=\"title\"\n :aria-label=\"ariaLabel\"\n :href=\"href\"\n data-testid=\"ci-icon\"\n @click=\"$emit('ciStatusBadgeClick')\"\n >\n <span class=\"ci-icon-gl-icon-wrapper\"><gl-icon :name=\"icon\" /></span\n ><span\n v-if=\"showStatusText\"\n class=\"gl-ml-2 gl-mr-3 gl-self-center gl-whitespace-nowrap gl-leading-1\"\n data-testid=\"ci-icon-text\"\n >{{ status.text }}</span\n >\n </component>\n</template>\n","import { render, staticRenderFns } from \"./ci_icon.vue?vue&type=template&id=884042fa\"\nimport script from \"./ci_icon.vue?vue&type=script&lang=js\"\nexport * from \"./ci_icon.vue?vue&type=script&lang=js\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","export const performanceMarkAndMeasure = ({ mark, measures = [] } = {}) => {\n window.requestAnimationFrame(() => {\n if (mark && !performance.getEntriesByName(mark).length) {\n performance.mark(mark);\n }\n measures.forEach((measure) => {\n performance.measure(measure.name, measure.start, measure.end);\n });\n });\n};\n","import { __, s__ } from '~/locale';\n\nexport const CANCEL_REQUEST = 'CANCEL_REQUEST';\nexport const SUPPORTED_FILTER_PARAMETERS = ['username', 'ref', 'status', 'source'];\nexport const NEEDS_PROPERTY = 'needs';\nexport const EXPLICIT_NEEDS_PROPERTY = 'previousStageJobsOrNeeds';\n\nexport const testStatus = {\n FAILED: 'failed',\n SKIPPED: 'skipped',\n SUCCESS: 'success',\n ERROR: 'error',\n UNKNOWN: 'unknown',\n};\n\n/* Error constants shared across graphs */\nexport const DEFAULT = 'default';\nexport const DELETE_FAILURE = 'delete_pipeline_failure';\nexport const DRAW_FAILURE = 'draw_failure';\nexport const LOAD_FAILURE = 'load_failure';\nexport const POST_FAILURE = 'post_failure';\n\n// Pipeline tabs\n\nexport const pipelineTabName = 'graph';\nexport const jobsTabName = 'builds';\nexport const failedJobsTabName = 'failures';\nexport const testReportTabName = 'test_report';\nexport const manualVariablesTabName = 'manual_variables';\nexport const securityTabName = 'security';\nexport const licensesTabName = 'licenses';\nexport const codeQualityTabName = 'codequality_report';\n\nexport const validPipelineTabNames = [\n jobsTabName,\n failedJobsTabName,\n testReportTabName,\n securityTabName,\n licensesTabName,\n codeQualityTabName,\n manualVariablesTabName,\n];\n\nexport const TOAST_MESSAGE = s__('Pipeline|Creating pipeline.');\n\nexport const DEFAULT_FIELDS = [\n {\n key: 'name',\n label: __('Name'),\n columnClass: 'gl-w-1/5',\n },\n {\n key: 'stage',\n label: __('Stage'),\n columnClass: 'gl-w-1/5',\n },\n {\n key: 'failureMessage',\n label: __('Failure'),\n columnClass: 'gl-w-2/5',\n },\n {\n key: 'actions',\n label: '',\n tdClass: 'gl-text-right',\n columnClass: 'gl-w-1/5',\n },\n];\n"],"sourceRoot":""}