2020-06-23 00:09:42 +05:30
< script >
2021-01-29 00:20:46 +05:30
import { GlAlert , GlButton , GlEmptyState , GlLink , GlSprintf } from '@gitlab/ui' ;
2020-07-28 23:09:34 +05:30
import { isEmpty } from 'lodash' ;
2020-06-23 00:09:42 +05:30
import { _ _ } from '~/locale' ;
2020-10-24 23:57:45 +05:30
import { fetchPolicies } from '~/lib/graphql' ;
import getDagVisData from '../../graphql/queries/get_dag_vis_data.query.graphql' ;
2020-06-23 00:09:42 +05:30
import DagGraph from './dag_graph.vue' ;
2020-07-28 23:09:34 +05:30
import DagAnnotations from './dag_annotations.vue' ;
2021-01-03 14:25:43 +05:30
import { ADD _NOTE , REMOVE _NOTE , REPLACE _NOTES } from './constants' ;
import { parseData } from '../parsing_utils' ;
import { DEFAULT , PARSE _FAILURE , LOAD _FAILURE , UNSUPPORTED _DATA } from '../../constants' ;
2020-06-23 00:09:42 +05:30
export default {
// eslint-disable-next-line @gitlab/require-i18n-strings
name : 'Dag' ,
components : {
2020-07-28 23:09:34 +05:30
DagAnnotations ,
2020-06-23 00:09:42 +05:30
DagGraph ,
GlAlert ,
2020-07-28 23:09:34 +05:30
GlButton ,
2021-01-29 00:20:46 +05:30
GlEmptyState ,
GlLink ,
GlSprintf ,
2020-06-23 00:09:42 +05:30
} ,
2020-10-24 23:57:45 +05:30
inject : {
2021-01-29 00:20:46 +05:30
aboutDagDocPath : {
default : null ,
} ,
2020-10-24 23:57:45 +05:30
dagDocPath : {
default : null ,
2020-06-23 00:09:42 +05:30
} ,
2020-07-28 23:09:34 +05:30
emptySvgPath : {
default : '' ,
} ,
2020-10-24 23:57:45 +05:30
pipelineIid : {
default : '' ,
} ,
pipelineProjectPath : {
2020-07-28 23:09:34 +05:30
default : '' ,
} ,
2020-06-23 00:09:42 +05:30
} ,
2020-10-24 23:57:45 +05:30
apollo : {
graphData : {
fetchPolicy : fetchPolicies . CACHE _AND _NETWORK ,
query : getDagVisData ,
variables ( ) {
return {
projectPath : this . pipelineProjectPath ,
iid : this . pipelineIid ,
} ;
} ,
update ( data ) {
const {
stages : { nodes : stages } ,
} = data . project . pipeline ;
const unwrappedGroups = stages
. map ( ( { name , groups : { nodes : groups } } ) => {
return groups . map ( group => {
return { category : name , ... group } ;
} ) ;
} )
. flat ( 2 ) ;
const nodes = unwrappedGroups . map ( group => {
const jobs = group . jobs . nodes . map ( ( { name , needs } ) => {
return { name , needs : needs . nodes . map ( need => need . name ) } ;
} ) ;
return { ... group , jobs } ;
} ) ;
return nodes ;
} ,
error ( ) {
this . reportFailure ( LOAD _FAILURE ) ;
} ,
} ,
} ,
2020-06-23 00:09:42 +05:30
data ( ) {
return {
2020-07-28 23:09:34 +05:30
annotationsMap : { } ,
2020-06-23 00:09:42 +05:30
failureType : null ,
graphData : null ,
2020-07-28 23:09:34 +05:30
showFailureAlert : false ,
hasNoDependentJobs : false ,
2020-06-23 00:09:42 +05:30
} ;
} ,
errorTexts : {
[ LOAD _FAILURE ] : _ _ ( 'We are currently unable to fetch data for this graph.' ) ,
[ PARSE _FAILURE ] : _ _ ( 'There was an error parsing the data for this graph.' ) ,
2020-07-28 23:09:34 +05:30
[ UNSUPPORTED _DATA ] : _ _ ( 'DAG visualization requires at least 3 dependent jobs.' ) ,
2020-06-23 00:09:42 +05:30
[ DEFAULT ] : _ _ ( 'An unknown error occurred while loading this graph.' ) ,
} ,
2020-07-28 23:09:34 +05:30
emptyStateTexts : {
2021-01-29 00:20:46 +05:30
title : _ _ ( 'Speed up your pipelines with Needs relationships' ) ,
2020-07-28 23:09:34 +05:30
firstDescription : _ _ (
2021-01-29 00:20:46 +05:30
'Using the %{codeStart}needs%{codeEnd} keyword makes jobs run before their stage is reached. Jobs run as soon as their %{codeStart}needs%{codeEnd} relationships are met, which speeds up your pipelines.' ,
2020-07-28 23:09:34 +05:30
) ,
secondDescription : _ _ (
2021-01-29 00:20:46 +05:30
"If you add %{codeStart}needs%{codeEnd} to jobs in your pipeline you'll be able to view the %{codeStart}needs%{codeEnd} relationships between jobs in this tab as a %{linkStart}Directed Acyclic Graph (DAG)%{linkEnd}." ,
2020-07-28 23:09:34 +05:30
) ,
2021-01-29 00:20:46 +05:30
button : _ _ ( 'Learn more about Needs relationships' ) ,
2020-07-28 23:09:34 +05:30
} ,
2020-06-23 00:09:42 +05:30
computed : {
failure ( ) {
switch ( this . failureType ) {
case LOAD _FAILURE :
return {
text : this . $options . errorTexts [ LOAD _FAILURE ] ,
variant : 'danger' ,
} ;
case PARSE _FAILURE :
return {
text : this . $options . errorTexts [ PARSE _FAILURE ] ,
variant : 'danger' ,
} ;
case UNSUPPORTED _DATA :
return {
text : this . $options . errorTexts [ UNSUPPORTED _DATA ] ,
variant : 'info' ,
} ;
default :
return {
text : this . $options . errorTexts [ DEFAULT ] ,
2020-10-24 23:57:45 +05:30
variant : 'danger' ,
2020-06-23 00:09:42 +05:30
} ;
}
} ,
2020-10-24 23:57:45 +05:30
processedData ( ) {
return this . processGraphData ( this . graphData ) ;
} ,
2020-07-28 23:09:34 +05:30
shouldDisplayAnnotations ( ) {
return ! isEmpty ( this . annotationsMap ) ;
} ,
2020-06-23 00:09:42 +05:30
shouldDisplayGraph ( ) {
2020-10-24 23:57:45 +05:30
return Boolean ( ! this . showFailureAlert && ! this . hasNoDependentJobs && this . graphData ) ;
2020-06-23 00:09:42 +05:30
} ,
} ,
methods : {
2020-07-28 23:09:34 +05:30
addAnnotationToMap ( { uid , source , target } ) {
this . $set ( this . annotationsMap , uid , { source , target } ) ;
} ,
2020-06-23 00:09:42 +05:30
processGraphData ( data ) {
let parsed ;
try {
2020-10-24 23:57:45 +05:30
parsed = parseData ( data ) ;
2020-06-23 00:09:42 +05:30
} catch {
this . reportFailure ( PARSE _FAILURE ) ;
2020-10-24 23:57:45 +05:30
return { } ;
2020-06-23 00:09:42 +05:30
}
2020-07-28 23:09:34 +05:30
if ( parsed . links . length === 1 ) {
2020-06-23 00:09:42 +05:30
this . reportFailure ( UNSUPPORTED _DATA ) ;
2020-10-24 23:57:45 +05:30
return { } ;
2020-06-23 00:09:42 +05:30
}
2020-07-28 23:09:34 +05:30
// If there are no links, we don't report failure
// as it simply means the user does not use job dependencies
if ( parsed . links . length === 0 ) {
this . hasNoDependentJobs = true ;
2020-10-24 23:57:45 +05:30
return { } ;
2020-07-28 23:09:34 +05:30
}
2020-10-24 23:57:45 +05:30
return parsed ;
2020-06-23 00:09:42 +05:30
} ,
hideAlert ( ) {
this . showFailureAlert = false ;
} ,
2020-07-28 23:09:34 +05:30
removeAnnotationFromMap ( { uid } ) {
this . $delete ( this . annotationsMap , uid ) ;
} ,
2020-06-23 00:09:42 +05:30
reportFailure ( type ) {
this . showFailureAlert = true ;
this . failureType = type ;
} ,
2020-07-28 23:09:34 +05:30
updateAnnotation ( { type , data } ) {
switch ( type ) {
case ADD _NOTE :
this . addAnnotationToMap ( data ) ;
break ;
case REMOVE _NOTE :
this . removeAnnotationFromMap ( data ) ;
break ;
case REPLACE _NOTES :
this . annotationsMap = data ;
break ;
default :
break ;
}
} ,
2020-06-23 00:09:42 +05:30
} ,
} ;
< / script >
< template >
< div >
< gl-alert v-if = "showFailureAlert" :variant="failure.variant" @dismiss="hideAlert" >
{ { failure . text } }
< / gl-alert >
2020-07-28 23:09:34 +05:30
< div class = "gl-relative" >
< dag-annotations v-if = "shouldDisplayAnnotations" :annotations="annotationsMap" / >
< dag-graph
v - if = "shouldDisplayGraph"
2020-10-24 23:57:45 +05:30
: graph - data = "processedData"
2020-07-28 23:09:34 +05:30
@ onFailure = "reportFailure"
@ update - annotation = "updateAnnotation"
/ >
< gl-empty-state
v - else - if = "hasNoDependentJobs"
: svg - path = "emptySvgPath"
: title = "$options.emptyStateTexts.title"
>
< template # description >
< div class = "gl-text-left" >
< p >
< gl-sprintf :message = "$options.emptyStateTexts.firstDescription" >
< template # code = "{ content }" >
< code > { { content } } < / code >
< / template >
< / gl-sprintf >
< / p >
< p >
< gl-sprintf :message = "$options.emptyStateTexts.secondDescription" >
< template # code = "{ content }" >
< code > { { content } } < / code >
< / template >
2021-01-29 00:20:46 +05:30
< template # link = "{ content }" >
< gl-link :href = "aboutDagDocPath" > { { content } } < / gl-link >
< / template >
2020-07-28 23:09:34 +05:30
< / gl-sprintf >
< / p >
< / div >
< / template >
2020-10-24 23:57:45 +05:30
< template v-if = "dagDocPath" # actions >
2020-07-28 23:09:34 +05:30
< gl-button :href = "dagDocPath" target = "__blank" variant = "success" >
{ { $options . emptyStateTexts . button } }
< / gl-button >
< / template >
< / gl-empty-state >
< / div >
2020-06-23 00:09:42 +05:30
< / div >
< / template >