Skip to content

Commit f9465bf

Browse files
tito12wslulciuc
andauthored
UI: Selection on graph with edges. Status for jobs/datasets (#2384)
* Selection on graph with edges. Status for jobs/datasets Signed-off-by: tito12 <vladyslav.sedenko@gmail.com> --------- Signed-off-by: tito12 <vladyslav.sedenko@gmail.com> Co-authored-by: Willy Lulciuc <willy@datakin.com>
1 parent 9e0c84b commit f9465bf

20 files changed

Lines changed: 329 additions & 163 deletions

File tree

docker/metadata.json

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1358,6 +1358,22 @@
13581358
"_schemaURL": "https://openlineage.io/spec/facets/1-0-0/DatasourceDatasetFacet.json",
13591359
"name": "food_delivery_db",
13601360
"uri": "postgres://food_delivery:food_delivery@postgres:5432/food_delivery"
1361+
},
1362+
"dataQualityAssertions": {
1363+
"_producer": "https://github.com/MarquezProject/marquez/blob/main/docker/metadata.json",
1364+
"_schemaURL": "https://openlineage.io/spec/facets/1-0-0/DataQualityAssertionsDatasetFacet.json",
1365+
"assertions": [
1366+
{
1367+
"assertion": "not_null",
1368+
"success": false,
1369+
"column": "driver_id"
1370+
},
1371+
{
1372+
"assertion": "is_string",
1373+
"success": true,
1374+
"column": "customer_address"
1375+
}
1376+
]
13611377
}
13621378
}
13631379
}
@@ -1824,4 +1840,4 @@
18241840
},
18251841
"producer": "https://github.com/MarquezProject/marquez/blob/main/docker/metadata.json"
18261842
}
1827-
]
1843+
]
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
import { theme } from '../../../helpers/theme'
4+
import Box from '@material-ui/core/Box'
5+
import MqText from '../text/MqText'
6+
import React from 'react'
7+
import createStyles from '@material-ui/core/styles/createStyles'
8+
import withStyles, { WithStyles } from '@material-ui/core/styles/withStyles'
9+
10+
const styles = () =>
11+
createStyles({
12+
type: {
13+
display: 'flex',
14+
alignItems: 'center',
15+
gap: theme.spacing(1)
16+
},
17+
status: {
18+
width: theme.spacing(2),
19+
height: theme.spacing(2),
20+
borderRadius: '50%'
21+
}
22+
})
23+
24+
interface OwnProps {
25+
color: string | null
26+
label?: string
27+
}
28+
29+
const MqStatus: React.FC<OwnProps & WithStyles<typeof styles>> = ({ label, color, classes }) => {
30+
if (!color) {
31+
return null
32+
}
33+
return (
34+
<Box className={classes.type}>
35+
<Box className={classes.status} style={{ backgroundColor: color }} />
36+
{label && <MqText>{label}</MqText>}
37+
</Box>
38+
)
39+
}
40+
41+
export default withStyles(styles)(MqStatus)

web/src/components/datasets/DatasetColumnLineage.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,4 @@ const mapDispatchToProps = (dispatch: Redux.Dispatch) =>
102102
dispatch
103103
)
104104

105-
export default connect(
106-
mapStateToProps,
107-
mapDispatchToProps
108-
)(DatasetColumnLineage)
105+
export default connect(mapStateToProps, mapDispatchToProps)(DatasetColumnLineage)

web/src/components/datasets/DatasetDetailPage.tsx

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { LineageDataset } from '../lineage/types'
1515
import { alpha } from '@material-ui/core/styles'
1616
import { bindActionCreators } from 'redux'
1717
import { connect } from 'react-redux'
18+
import { datasetFacetsStatus } from '../../helpers/nodes'
1819
import {
1920
deleteDataset,
2021
dialogToggle,
@@ -31,7 +32,9 @@ import DatasetInfo from './DatasetInfo'
3132
import DatasetVersions from './DatasetVersions'
3233
import Dialog from '../Dialog'
3334
import IconButton from '@material-ui/core/IconButton'
35+
import MqStatus from '../core/status/MqStatus'
3436
import MqText from '../core/text/MqText'
37+
3538
import React, { ChangeEvent, FunctionComponent, SetStateAction, useEffect } from 'react'
3639

3740
const styles = ({ spacing }: ITheme) => {
@@ -143,6 +146,7 @@ const DatasetDetailPage: FunctionComponent<IProps> = props => {
143146

144147
const firstVersion = versions[0]
145148
const { name, tags, description } = firstVersion
149+
const facetsStatus = datasetFacetsStatus(firstVersion.facets)
146150

147151
return (
148152
<Box my={2} className={root}>
@@ -202,9 +206,16 @@ const DatasetDetailPage: FunctionComponent<IProps> = props => {
202206
</IconButton>
203207
</Box>
204208
</Box>
205-
<MqText heading font={'mono'}>
206-
{name}
207-
</MqText>
209+
<Box display={'flex'} alignItems={'center'}>
210+
{facetsStatus && (
211+
<Box mr={1}>
212+
<MqStatus color={facetsStatus} />
213+
</Box>
214+
)}
215+
<MqText heading font={'mono'}>
216+
{name}
217+
</MqText>
218+
</Box>
208219
<Box mb={2}>
209220
<MqText subdued>{description}</MqText>
210221
</Box>
@@ -241,7 +252,4 @@ const mapDispatchToProps = (dispatch: Redux.Dispatch) =>
241252
dispatch
242253
)
243254

244-
export default connect(
245-
mapStateToProps,
246-
mapDispatchToProps
247-
)(withStyles(styles)(DatasetDetailPage))
255+
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(DatasetDetailPage))

web/src/components/datasets/DatasetVersions.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,8 @@ interface DatasetVersionsProps {
3131
versions: DatasetVersion[]
3232
}
3333

34-
const DatasetVersions: FunctionComponent<
35-
DatasetVersionsProps & IWithStyles<typeof styles>
36-
> = props => {
34+
const DatasetVersions: FunctionComponent<DatasetVersionsProps &
35+
IWithStyles<typeof styles>> = props => {
3736
const { versions, classes } = props
3837

3938
const [infoView, setInfoView] = React.useState<DatasetVersion | null>(null)

web/src/components/jobs/JobDetailPage.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,16 @@ import {
2525
resetJobs,
2626
resetRuns
2727
} from '../../store/actionCreators'
28+
import { jobRunsStatus } from '../../helpers/nodes'
2829
import { theme } from '../../helpers/theme'
2930
import { useHistory } from 'react-router-dom'
3031
import CloseIcon from '@material-ui/icons/Close'
3132
import Dialog from '../Dialog'
3233
import IconButton from '@material-ui/core/IconButton'
3334
import MqEmpty from '../core/empty/MqEmpty'
35+
import MqStatus from '../core/status/MqStatus'
3436
import MqText from '../core/text/MqText'
3537
import RunInfo from './RunInfo'
36-
import RunStatus from './RunStatus'
3738
import Runs from './Runs'
3839

3940
const styles = ({ spacing }: ITheme) => {
@@ -166,9 +167,9 @@ const JobDetailPage: FunctionComponent<IProps> = props => {
166167
</Box>
167168
</Box>
168169
<Box display={'flex'} alignItems={'center'}>
169-
{job.latestRun && (
170+
{runs.length && (
170171
<Box mr={1}>
171-
<RunStatus run={job.latestRun} />
172+
<MqStatus color={jobRunsStatus(runs)} />
172173
</Box>
173174
)}
174175
<MqText font={'mono'} heading>
@@ -212,7 +213,4 @@ const mapDispatchToProps = (dispatch: Redux.Dispatch) =>
212213
dispatch
213214
)
214215

215-
export default connect(
216-
mapStateToProps,
217-
mapDispatchToProps
218-
)(withStyles(styles)(JobDetailPage))
216+
export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(JobDetailPage))

web/src/components/jobs/RunStatus.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33

44
import { Box, Theme, Tooltip, WithStyles, createStyles, withStyles } from '@material-ui/core'
55
import { Run } from '../../types/api'
6-
7-
import { runColorMap } from '../../helpers/runs'
6+
import { runStateColor } from '../../helpers/nodes'
87

98
import React, { FunctionComponent } from 'react'
109

@@ -26,7 +25,11 @@ const RunStatus: FunctionComponent<RunStatusProps & WithStyles<typeof styles>> =
2625
const { run, classes } = props
2726
return (
2827
<Tooltip title={run.state}>
29-
<Box mr={1} className={classes.status} style={{ backgroundColor: runColorMap[run.state] }} />
28+
<Box
29+
mr={1}
30+
className={classes.status}
31+
style={{ backgroundColor: runStateColor(run.state) }}
32+
/>
3033
</Tooltip>
3134
)
3235
}

web/src/components/lineage/Lineage.tsx

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ class Lineage extends React.Component<LineageProps, LineageState> {
8585
const nodeName = this.props.match.params.nodeName
8686
const namespace = this.props.match.params.namespace
8787
const nodeType = this.props.match.params.nodeType
88+
8889
if (nodeName && namespace && nodeType) {
8990
const nodeId = generateNodeId(
9091
this.props.match.params.nodeType.toUpperCase() as JobOrDataset,
@@ -114,6 +115,7 @@ class Lineage extends React.Component<LineageProps, LineageState> {
114115
this.props.match.params.namespace,
115116
this.props.match.params.nodeName
116117
)
118+
this.getEdges()
117119
}
118120
}
119121

@@ -129,6 +131,48 @@ class Lineage extends React.Component<LineageProps, LineageState> {
129131
})
130132
}
131133

134+
getEdges = () => {
135+
const selectedPaths = this.getSelectedPaths()
136+
137+
return g?.edges().map(e => {
138+
const isSelected = selectedPaths.some((r: any) => e.v === r[0] && e.w === r[1])
139+
return Object.assign(g.edge(e), { isSelected: isSelected })
140+
})
141+
}
142+
143+
getSelectedPaths = () => {
144+
const paths = [] as Array<[string, string]>
145+
146+
const getSuccessors = (node: string) => {
147+
const successors = g?.successors(node)
148+
if (successors?.length) {
149+
for (let i = 0; i < node.length - 1; i++) {
150+
if (successors[i]) {
151+
paths.push([node, (successors[i] as unknown) as string])
152+
getSuccessors((successors[i] as unknown) as string)
153+
}
154+
}
155+
}
156+
}
157+
158+
const getPredecessors = (node: string) => {
159+
const predecessors = g?.predecessors(node)
160+
if (predecessors?.length) {
161+
for (let i = 0; i < node.length - 1; i++) {
162+
if (predecessors[i]) {
163+
paths.push([(predecessors[i] as unknown) as string, node])
164+
getPredecessors((predecessors[i] as unknown) as string)
165+
}
166+
}
167+
}
168+
}
169+
170+
getSuccessors(this.props.selectedNode)
171+
getPredecessors(this.props.selectedNode)
172+
173+
return paths
174+
}
175+
132176
buildGraphAll = (graph: LineageNode[]) => {
133177
// nodes
134178
for (let i = 0; i < graph.length; i++) {
@@ -150,14 +194,15 @@ class Lineage extends React.Component<LineageProps, LineageState> {
150194

151195
this.setState({
152196
graph: g,
153-
edges: g.edges().map(e => g.edge(e)),
197+
edges: this.getEdges(),
154198
nodes: g.nodes().map(v => g.node(v))
155199
})
156200
}
157201

158202
render() {
159203
const { classes } = this.props
160204
const i18next = require('i18next')
205+
161206
return (
162207
<Box className={classes.lineageContainer}>
163208
{this.props.selectedNode === null && (
@@ -227,9 +272,6 @@ class Lineage extends React.Component<LineageProps, LineageState> {
227272
<Node
228273
key={node.data.name}
229274
node={node}
230-
edgeEnds={this.state.edges.map(
231-
edge => edge.points[edge.points.length - 1]
232-
)}
233275
selectedNode={this.props.selectedNode}
234276
/>
235277
))}
@@ -262,9 +304,4 @@ const mapDispatchToProps = (dispatch: Redux.Dispatch) =>
262304
dispatch
263305
)
264306

265-
export default withStyles(styles)(
266-
connect(
267-
mapStateToProps,
268-
mapDispatchToProps
269-
)(withRouter(Lineage))
270-
)
307+
export default withStyles(styles)(connect(mapStateToProps, mapDispatchToProps)(withRouter(Lineage)))

web/src/components/lineage/components/drag-bar/DragBar.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,4 @@ const mapDispatchToProps = (dispatch: Redux.Dispatch) =>
107107
},
108108
dispatch
109109
)
110-
export default connect(
111-
null,
112-
mapDispatchToProps
113-
)(withStyles(styles)(DragBar))
110+
export default connect(null, mapDispatchToProps)(withStyles(styles)(DragBar))

web/src/components/lineage/components/edge/Edge.tsx

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,48 @@
11
// Copyright 2018-2023 contributors to the Marquez project
22
// SPDX-License-Identifier: Apache-2.0
33

4+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
45
import { GraphEdge } from 'dagre'
56
import { LinePath } from '@visx/shape'
67
import { curveMonotoneX } from '@visx/curve'
8+
import { faCaretRight } from '@fortawesome/free-solid-svg-icons/faCaretRight'
79
import { theme } from '../../../../helpers/theme'
810
import React from 'react'
911

1012
type EdgeProps = {
1113
edgePoints: GraphEdge[]
1214
}
1315

16+
type EdgePoint = {
17+
isSelected: boolean
18+
points: {
19+
x: number
20+
y: number
21+
}[]
22+
}
23+
24+
const RADIUS = 14
25+
const OUTER_RADIUS = RADIUS + 8
26+
const ICON_SIZE = 16
27+
1428
class Edge extends React.Component<EdgeProps> {
29+
getPoints = (edge: EdgePoint) => edge.points[edge.points.length - 1]
30+
1531
render() {
1632
const { edgePoints } = this.props
33+
const edgeEnds = edgePoints.map(edge => {
34+
const isSelected = edgePoints.find(
35+
o =>
36+
this.getPoints(o as EdgePoint).x == this.getPoints(edge as EdgePoint).x &&
37+
this.getPoints(o as EdgePoint).y == this.getPoints(edge as EdgePoint).y &&
38+
o.isSelected === true
39+
)
40+
return {
41+
...edge.points[edge.points.length - 1],
42+
...{ isSelected: typeof isSelected !== 'undefined' }
43+
}
44+
})
45+
1746
return (
1847
<>
1948
{edgePoints.map((edge, i) => (
@@ -23,12 +52,23 @@ class Edge extends React.Component<EdgeProps> {
2352
data={edge.points}
2453
x={(d, index) => (index === 0 ? d.x + 20 : d.x - 25)}
2554
y={d => d.y}
26-
stroke={theme.palette.secondary.main}
55+
stroke={edge.isSelected ? theme.palette.common.white : theme.palette.secondary.main}
2756
strokeWidth={1}
2857
opacity={1}
2958
shapeRendering='geometricPrecision'
3059
/>
3160
))}
61+
{edgeEnds.map((edge, i) => (
62+
<FontAwesomeIcon
63+
key={i}
64+
icon={faCaretRight}
65+
x={edge.x - OUTER_RADIUS - ICON_SIZE / 2}
66+
y={edge.y - ICON_SIZE / 2}
67+
width={ICON_SIZE}
68+
height={ICON_SIZE}
69+
color={edge.isSelected ? theme.palette.common.white : theme.palette.secondary.main}
70+
/>
71+
))}
3272
</>
3373
)
3474
}

0 commit comments

Comments
 (0)