diff options
author | Zuul <zuul@review.opendev.org> | 2022-08-16 19:37:39 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2022-08-16 19:37:39 +0000 |
commit | a425f56d08dd577539ca34418a28f7c0c68e4162 (patch) | |
tree | 126b575ce294f8d361e1aa86f8ac146a7d2bb412 /web/src/containers/jobgraph/JobGraphDisplay.jsx | |
parent | 598e7a9632b01e31e6611ed47198214128f7394d (diff) | |
parent | 97376adc21787494f5e544b271c7b435950d6256 (diff) | |
download | zuul-a425f56d08dd577539ca34418a28f7c0c68e4162.tar.gz |
Merge "Add job graph support to web UI"
Diffstat (limited to 'web/src/containers/jobgraph/JobGraphDisplay.jsx')
-rw-r--r-- | web/src/containers/jobgraph/JobGraphDisplay.jsx | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/web/src/containers/jobgraph/JobGraphDisplay.jsx b/web/src/containers/jobgraph/JobGraphDisplay.jsx new file mode 100644 index 000000000..1fbcef332 --- /dev/null +++ b/web/src/containers/jobgraph/JobGraphDisplay.jsx @@ -0,0 +1,120 @@ +// Copyright 2022 Acme Gating, LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +import React, { useState, useEffect} from 'react' +import PropTypes from 'prop-types' +import { connect } from 'react-redux' +import * as d3 from 'd3' + +import { makeJobGraphKey, fetchJobGraphIfNeeded } from '../../actions/jobgraph' +import { graphviz } from 'd3-graphviz' + +function makeDot(jobGraph) { + let ret = 'digraph job_graph {' + ret += ' rankdir=LR;\n' + ret += ' node [shape=box];\n' + jobGraph.forEach((job) => { + if (job.dependencies.length) { + job.dependencies.forEach((dep) => { + let soft = ' [dir=back]' + if (dep.soft) { + soft = ' [style=dashed dir=back]' + } + ret += ' "' + dep.name + '" -> "' + job.name + '"' + soft + ';\n' + }) + } else { + ret += ' "' + job.name + '";\n' + } + }) + ret += '}\n' + return ret +} + +function GraphViz(props) { + useEffect(() => { + const gv = graphviz('#graphviz') + .options({ + fit: false, + zoom: true, + tweenPaths: false, + scale: 0.75, + }).renderDot(props.dot) + + // Fix up the initial values of the internal transform data; + // without this the first time we pan the graph jumps. + const element = d3.select('.zuul-job-graph > svg') + const transform = element[0][0].firstElementChild.attributes.transform.value + const match = transform.match(/translate\(\d+,(\d+)\).*/) + if (match && match.length > 0) { + const val = parseInt(match[1]) + gv._translation.y = val + gv._originalTransform.y = val + } + }, [props.dot]) + + return ( + <div className="zuul-job-graph" id="graphviz"/> + ) +} + +GraphViz.propTypes = { + dot: PropTypes.string.isRequired, +} + +function JobGraphDisplay(props) { + const [dot, setDot] = useState() + const {fetchJobGraphIfNeeded, tenant, project, pipeline, branch} = props + + useEffect(() => { + fetchJobGraphIfNeeded(tenant, project.name, pipeline, branch) + }, [fetchJobGraphIfNeeded, tenant, project, pipeline, branch]) + + const tenantJobGraph = props.jobgraph.jobGraphs[props.tenant.name] + const jobGraphKey = makeJobGraphKey(props.project.name, + props.pipeline, + props.branch) + const jobGraph = tenantJobGraph ? tenantJobGraph[jobGraphKey] : undefined + useEffect(() => { + if (jobGraph) { + setDot(makeDot(jobGraph)) + } + }, [jobGraph]) + return ( + <> + {dot && <GraphViz dot={dot}/>} + </> + ) +} + +JobGraphDisplay.propTypes = { + fetchJobGraphIfNeeded: PropTypes.func, + tenant: PropTypes.object, + project: PropTypes.object.isRequired, + pipeline: PropTypes.string.isRequired, + branch: PropTypes.string.isRequired, + jobgraph: PropTypes.object, + dispatch: PropTypes.func, + state: PropTypes.object, +} +function mapStateToProps(state) { + return { + tenant: state.tenant, + jobgraph: state.jobgraph, + state: state, + } +} + +const mapDispatchToProps = { fetchJobGraphIfNeeded } + +export default connect(mapStateToProps, mapDispatchToProps)(JobGraphDisplay) |