Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding the IO Tab #2613

Merged
merged 5 commits into from
Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions web/src/components/core/text/MqText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ interface OwnProps {
small?: boolean
bottomMargin?: boolean
children: ReactElement | (string | ReactElement)[] | string | string[] | number | undefined | null
onClick?: () => void
}

type MqTextProps = OwnProps
Expand All @@ -53,6 +54,7 @@ const MqText: React.FC<MqTextProps> = ({
highlight,
color,
small,
onClick,
}) => {
const theme = createTheme(useTheme())

Expand Down Expand Up @@ -143,6 +145,7 @@ const MqText: React.FC<MqTextProps> = ({
if (heading) {
return (
<Typography
onClick={onClick}
variant='h4'
sx={Object.assign(classesObject.root, classesObject.heading, conditionalClasses)}
style={style}
Expand All @@ -152,7 +155,12 @@ const MqText: React.FC<MqTextProps> = ({
)
} else if (link && linkTo) {
return (
<LinkRouter to={linkTo} aria-disabled={disabled} style={{ textDecoration: 'none' }}>
<LinkRouter
to={linkTo}
aria-disabled={disabled}
style={{ textDecoration: 'none' }}
onClick={onClick}
>
<Box
component='span'
sx={Object.assign(classesObject.root, classesObject.link, conditionalClasses)}
Expand All @@ -164,6 +172,7 @@ const MqText: React.FC<MqTextProps> = ({
} else if (link && href) {
return (
<Link
onClick={onClick}
href={href}
target={'_blank'}
rel='noopener noreferrer'
Expand All @@ -174,7 +183,11 @@ const MqText: React.FC<MqTextProps> = ({
)
} else {
return (
<Box sx={Object.assign(classesObject.root, conditionalClasses)} style={style}>
<Box
onClick={onClick}
sx={Object.assign(classesObject.root, conditionalClasses)}
style={style}
>
{children}
</Box>
)
Expand Down
34 changes: 24 additions & 10 deletions web/src/components/datasets/DatasetDetailPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
fetchDatasetVersions,
resetDataset,
resetDatasetVersions,
setTabIndex,
} from '../../store/actionCreators'
import { useNavigate } from 'react-router-dom'
import CloseIcon from '@mui/icons-material/Close'
Expand All @@ -29,14 +30,16 @@ import MqStatus from '../core/status/MqStatus'
import MqText from '../core/text/MqText'

import { useTheme } from '@emotion/react'
import React, { ChangeEvent, FunctionComponent, SetStateAction, useEffect } from 'react'
import Io from '../io/Io'
import React, { ChangeEvent, FunctionComponent, useEffect } from 'react'

interface StateProps {
lineageDataset: LineageDataset
versions: DatasetVersion[]
versionsLoading: boolean
datasets: IState['datasets']
display: IState['display']
tabIndex: IState['lineage']['tabIndex']
}

interface DispatchProps {
Expand All @@ -45,6 +48,7 @@ interface DispatchProps {
resetDataset: typeof resetDataset
deleteDataset: typeof deleteDataset
dialogToggle: typeof dialogToggle
setTabIndex: typeof setTabIndex
}

type IProps = StateProps & DispatchProps
Expand All @@ -68,6 +72,8 @@ const DatasetDetailPage: FunctionComponent<IProps> = (props) => {
versions,
versionsLoading,
lineageDataset,
tabIndex,
setTabIndex,
} = props
const navigate = useNavigate()
const i18next = require('i18next')
Expand All @@ -92,9 +98,8 @@ const DatasetDetailPage: FunctionComponent<IProps> = (props) => {
[]
)

const [tab, setTab] = React.useState(0)
const handleChange = (event: ChangeEvent, newValue: SetStateAction<number>) => {
setTab(newValue)
const handleChange = (_: ChangeEvent, newValue: number) => {
setTabIndex(newValue)
}

if (versionsLoading) {
Expand Down Expand Up @@ -149,20 +154,26 @@ const DatasetDetailPage: FunctionComponent<IProps> = (props) => {
)}
<Box display={'flex'} justifyContent={'space-between'} mb={2}>
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
<Tabs value={tab} onChange={handleChange} textColor='primary' indicatorColor='primary'>
<Tabs
value={tabIndex}
onChange={handleChange}
textColor='primary'
indicatorColor='primary'
>
<Tab
label={i18next.t('datasets.latest_tab')}
{...a11yProps(0)}
disableRipple={true}
/>
<Tab label={'I/O'} {...a11yProps(1)} disableRipple={true} />
<Tab
label={i18next.t('datasets.history_tab')}
{...a11yProps(1)}
{...a11yProps(2)}
disableRipple={true}
/>
<Tab
label={i18next.t('datasets.column_lineage_tab')}
{...a11yProps(1)}
{...a11yProps(3)}
disableRipple={true}
/>
</Tabs>
Expand Down Expand Up @@ -214,15 +225,16 @@ const DatasetDetailPage: FunctionComponent<IProps> = (props) => {
<MqText subdued>{description}</MqText>
</Box>
</Box>
{tab === 0 && (
{tabIndex === 0 && (
<DatasetInfo
datasetFields={firstVersion.fields}
facets={firstVersion.facets}
run={firstVersion.createdByRun}
/>
)}
{tab === 1 && <DatasetVersions versions={props.versions} />}
{tab === 2 && <DatasetColumnLineage lineageDataset={props.lineageDataset} />}
{tabIndex === 1 && <Io />}
{tabIndex === 2 && <DatasetVersions versions={props.versions} />}
{tabIndex === 3 && <DatasetColumnLineage lineageDataset={props.lineageDataset} />}
</Box>
)
}
Expand All @@ -232,6 +244,7 @@ const mapStateToProps = (state: IState) => ({
display: state.display,
versions: state.datasetVersions.result.versions,
versionsLoading: state.datasetVersions.isLoading,
tabIndex: state.lineage.tabIndex,
})

const mapDispatchToProps = (dispatch: Redux.Dispatch) =>
Expand All @@ -242,6 +255,7 @@ const mapDispatchToProps = (dispatch: Redux.Dispatch) =>
resetDataset: resetDataset,
deleteDataset: deleteDataset,
dialogToggle: dialogToggle,
setTabIndex: setTabIndex,
},
dispatch
)
Expand Down
150 changes: 150 additions & 0 deletions web/src/components/io/Io.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// Copyright 2018-2023 contributors to the Marquez project
// SPDX-License-Identifier: Apache-2.0

import * as Redux from 'redux'
import { Box } from '@mui/system'
import { IState } from '../../store/reducers'
import { LineageEdge, LineageNode } from '../lineage/types'

import { Table, TableBody, TableCell, TableHead, TableRow } from '@mui/material'
import { Undefinable } from '../../types/util/Nullable'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { encodeNode } from '../../helpers/nodes'
import { setSelectedNode } from '../../store/actionCreators'
import MqEmpty from '../core/empty/MqEmpty'
import MqText from '../core/text/MqText'
import React, { FunctionComponent } from 'react'

export interface DispatchProps {
setSelectedNode: typeof setSelectedNode
}

interface IOProps {
node: Undefinable<LineageNode>
inputs: Undefinable<LineageEdge[]>
outputs: Undefinable<LineageEdge[]>
}

function determineName(node: string) {
const colonIndex1 = node.indexOf(':')
if (colonIndex1 !== -1) {
const colonIndex2 = node.indexOf(':', colonIndex1 + 1)
if (colonIndex2 !== -1) {
return node.substring(colonIndex2 + 1)
}
}
return ''
}

export const determineLink = (current: LineageNode, edge: string) => {
return `/lineage/${encodeNode(
current.type === 'JOB' ? 'DATASET' : 'JOB',
edge.split(':')[1],
determineName(edge)
)}`
}

const Io: FunctionComponent<IOProps & DispatchProps> = ({
node,
inputs,
outputs,
setSelectedNode,
}) => {
const i18next = require('i18next')
if (!node) {
return null
}

return (
<Box display={'flex'}>
<Box width={'50%'}>
<Table sx={{ p: 2, mr: 1 }}>
<TableHead>
<TableRow>
<TableCell>
<MqText bold>INPUTS</MqText>
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{inputs?.map((input) => (
<TableRow key={input.origin}>
<TableCell>
<MqText
link
linkTo={determineLink(node, input.origin)}
onClick={() => {
setSelectedNode(input.origin)
}}
>
{determineName(input.origin)}
</MqText>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
{inputs && inputs.length === 0 && (
<Box mt={2}>
<MqEmpty title={i18next.t('lineage.empty')}>
<MqText subdued>{i18next.t('lineage.no_inputs')}</MqText>
</MqEmpty>
</Box>
)}
</Box>
<Box width={'50%'}>
<Table sx={{ p: 2, ml: 1 }}>
<TableHead>
<TableRow>
<TableCell>
<MqText bold>OUTPUTS</MqText>
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{outputs?.map((output) => (
<TableRow key={output.destination}>
<TableCell>
<MqText
link
linkTo={determineLink(node, output.destination)}
onClick={() => setSelectedNode(output.destination)}
>
{determineName(output.destination)}
</MqText>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
{outputs && outputs.length === 0 && (
<Box mt={2}>
<MqEmpty title={i18next.t('lineage.empty')}>
<MqText subdued>{i18next.t('lineage.no_outputs')}</MqText>
</MqEmpty>
</Box>
)}
</Box>
</Box>
)
}

const mapStateToProps = (state: IState) => {
const node = state.lineage.lineage.graph.find((node) => node.id === state.lineage.selectedNode)
return {
node: node,
inputs: node?.inEdges,
outputs: node?.outEdges,
}
}

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

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