Skip to content

Commit

Permalink
UI: add full graph toggle (#2569)
Browse files Browse the repository at this point in the history
* lint

Signed-off-by: John Lukenoff <jlukenoff@gmail.com>

* refactor

Signed-off-by: John Lukenoff <jlukenoff@gmail.com>

* fix test

Signed-off-by: John Lukenoff <jlukenoff@gmail.com>

* add tests

Signed-off-by: John Lukenoff <johnlukenoff@asana.com>

* convert depthconfig to formcontrol label

Signed-off-by: John Lukenoff <johnlukenoff@asana.com>

* unused import

Signed-off-by: John Lukenoff <johnlukenoff@asana.com>

---------

Signed-off-by: John Lukenoff <jlukenoff@gmail.com>
Signed-off-by: John Lukenoff <johnlukenoff@asana.com>
  • Loading branch information
jlukenoff authored Aug 8, 2023
1 parent 1d46b66 commit 86f957f
Show file tree
Hide file tree
Showing 8 changed files with 258 additions and 143 deletions.
160 changes: 70 additions & 90 deletions web/src/__tests__/components/Lineage.test.tsx
Original file line number Diff line number Diff line change
@@ -1,97 +1,64 @@
// Copyright 2018-2023 contributors to the Marquez project
// SPDX-License-Identifier: Apache-2.0

import React from 'react'
import Lineage, { LineageProps, getSelectedPaths, initGraph, buildGraphAll } from '../../components/lineage/Lineage'
import { LineageNode } from '../../components/lineage/types'
import { render } from '@testing-library/react'
import { createBrowserHistory } from 'history'
import createSagaMiddleware from 'redux-saga'
import { createRouterMiddleware } from '@lagunovsky/redux-react-router'
import createRootReducer from '../../store/reducers'
import { composeWithDevTools } from '@redux-devtools/extension'
import { applyMiddleware, createStore } from 'redux'
import { Provider } from 'react-redux'
import { MqNode } from '../../components/lineage/types'
import { getSelectedPaths, initGraph, buildGraphAll } from '../../components/lineage/Lineage'
import { LineageNode, MqNode } from '../../components/lineage/types'
import { graphlib } from 'dagre'
import rootSaga from '../../store/sagas'

const mockGraphWithCycle = [
{
id: 'job_foo',
inEdges: [
{
origin: 'dataset_foo',
destination: 'job_foo'
}
],
outEdges: [
{
origin: 'job_foo',
destination: 'dataset_bar'
}
]
},
{
id: 'dataset_bar',
inEdges: [
{
origin: 'job_foo',
destination: 'dataset_bar'
}
],
outEdges: [
{
origin: 'dataset_bar',
destination: 'job_bar'
}
]
},
{
id: 'job_bar',
inEdges: [
{
origin: 'dataset_bar',
destination: 'job_bar'
}
],
outEdges: [
{
origin: 'job_bar',
destination: 'dataset_foo'
}
]
},
{
id: 'dataset_foo',
inEdges: [
{
origin: 'job_bar',
destination: 'dataset_foo'
}
],
outEdges: [
{
origin: 'dataset_foo',
destination: 'job_foo'
}
]
class MockEdge {
origin: string
destination: string

constructor(origin, destination) {
this.origin = origin
this.destination = destination
}
]
}

class MockNode implements Partial<LineageNode> {
id: string
inEdges: MockEdge[]
outEdges: MockEdge[]

constructor(id, prev: string[], next: string[]) {
this.id = id
this.inEdges = prev ? prev.map(p => new MockEdge(p, id)) : ([] as MockEdge[])
this.outEdges = next ? next.map(n => new MockEdge(id, n)) : ([] as MockEdge[])
}
}

const mockGraphWithCycle = [
new MockNode('1', ['3'], ['2']),
new MockNode('2', ['1'], ['3']),
new MockNode('3', ['2'], ['1'])
] as LineageNode[]

const mockGraphWithoutCycle = [
new MockNode('1', [], ['2', '4']),
new MockNode('2', ['1'], ['3']),
new MockNode('3', ['2'], []),
new MockNode('4', ['1'], [])
] as LineageNode[]

describe('Lineage Component', () => {
const selectedNode = 'job_foo'
let g: graphlib.Graph<MqNode>
const selectedNode = '1'
let graphWithCycle: graphlib.Graph<MqNode>

beforeEach(() => {
g = initGraph()
buildGraphAll(g, mockGraphWithCycle, (gResult: graphlib.Graph<MqNode>) => {
g = gResult
})
graphWithCycle = initGraph()
buildGraphAll(
graphWithCycle,
mockGraphWithCycle,
true,
selectedNode,
(gResult: graphlib.Graph<MqNode>) => {
graphWithCycle = gResult
}
)
})

it("doesn't follow cycles in the lineage graph", () => {
const paths = getSelectedPaths(g, selectedNode)
const paths = getSelectedPaths(graphWithCycle, selectedNode)

const pathCounts = paths.reduce((acc, p) => {
const pathId = p.join(':')
Expand All @@ -103,19 +70,32 @@ describe('Lineage Component', () => {
})

it('renders a valid cycle', () => {
const actualPaths = getSelectedPaths(g, selectedNode)
const actualPaths = getSelectedPaths(graphWithCycle, selectedNode)

const expectedPaths = [
['job_foo', 'dataset_bar'],
['dataset_bar', 'job_bar'],
['job_bar', 'dataset_foo'],
['dataset_foo', 'job_foo'],
['dataset_foo', 'job_foo'],
['job_bar', 'dataset_foo'],
['dataset_bar', 'job_bar'],
['job_foo', 'dataset_bar']
['1', '2'],
['2', '3'],
['3', '1'],
['3', '1'],
['2', '3'],
['1', '2']
]

expect(actualPaths).toEqual(expectedPaths)
})

it('includes nodes in selected path when fullGraph is true', () => {
const g = initGraph()
buildGraphAll(g, mockGraphWithoutCycle, true, '3', () => null)

expect(g.node('4')).toBeDefined()
})

it('exclude nodes not in selected path when fullGraph is false', () => {
const g = initGraph()

buildGraphAll(g, mockGraphWithoutCycle, false, '3', () => null)

expect(g.node('4')).toBeUndefined()
})
})
59 changes: 59 additions & 0 deletions web/src/components/full-graph-switch/FullGraphSwitch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright 2018-2023 contributors to the Marquez project
// SPDX-License-Identifier: Apache-2.0

import * as Redux from 'redux'
import { Box, FormControlLabel, Switch } from '@mui/material'
import { IState } from '../../store/reducers'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { setShowFullGraph } from '../../store/actionCreators'
import React from 'react'

interface FullGraphSwitch {
showFullGraph: boolean
setShowFullGraph: (showFullGraph: boolean) => void
}

const FullGraphSwitch: React.FC<FullGraphSwitch> = ({ setShowFullGraph, showFullGraph }) => {
const i18next = require('i18next')
const FULL_GRAPH_LABEL = i18next.t('lineage.full_graph_label')
return (
<Box
sx={theme => ({
display: 'flex',
justifyContent: 'space-evenly',
alignItems: 'center',
zIndex: theme.zIndex.appBar
})}
>
<FormControlLabel
sx={{
marginLeft: 0
}}
labelPlacement='start'
control={
<Switch
checked={showFullGraph}
onChange={(_, checked) => setShowFullGraph(checked)}
color='primary'
/>
}
label={FULL_GRAPH_LABEL}
/>
</Box>
)
}

const mapStateToProps = (state: IState) => ({
showFullGraph: state.lineage.showFullGraph
})

const mapDispatchToProps = (dispatch: Redux.Dispatch) =>
bindActionCreators(
{
setShowFullGraph: setShowFullGraph
},
dispatch
)

export default connect(mapStateToProps, mapDispatchToProps)(FullGraphSwitch)
Loading

0 comments on commit 86f957f

Please sign in to comment.