Skip to content
Merged
6 changes: 5 additions & 1 deletion api/src/main/java/marquez/api/JobResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,8 @@ public Response listRuns(

final List<Run> runs =
runService.findAll(namespaceName.getValue(), jobName.getValue(), limit, offset);
return Response.ok(new Runs(runs)).build();
final int totalCount = jobService.countJobRuns(namespaceName.getValue(), jobName.getValue());
return Response.ok(new Runs(runs, totalCount)).build();
}

@Path("/jobs/runs/{id}")
Expand Down Expand Up @@ -329,5 +330,8 @@ public static class Runs {
@NonNull
@JsonProperty("runs")
List<Run> value;

@JsonProperty("totalCount")
int totalCount;
}
}
14 changes: 14 additions & 0 deletions api/src/main/java/marquez/db/JobDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,20 @@ job_tags as (
@SqlQuery("SELECT count(*) FROM jobs_view AS j WHERE symlink_target_uuid IS NULL")
int count();

@SqlQuery(
"""
select
count(*)
from
runs
where
namespace_name = :namespaceName
and
job_name = :job
;
""")
int countJobRuns(String namespaceName, String job);

@SqlQuery(
"SELECT count(*) FROM jobs_view AS j WHERE j.namespace_name = :namespaceName\n"
+ "AND symlink_target_uuid IS NULL")
Expand Down
1 change: 1 addition & 0 deletions api/src/test/java/marquez/db/JobDaoTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ public void testCountFor() {
assertThat(jobDao.count()).isEqualTo(4);

assertThat(jobDao.countFor(namespace.getName())).isEqualTo(3);
assertThat(jobDao.countJobRuns(namespace.getName(), "targetJob")).isEqualTo(0);
}

@Test
Expand Down
46 changes: 41 additions & 5 deletions web/src/components/jobs/JobDetailPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import Dialog from '../Dialog'
import IconButton from '@mui/material/IconButton'
import JobTags from './JobTags'
import MqEmpty from '../core/empty/MqEmpty'
import MqPaging from '../paging/MqPaging'
import MqStatus from '../core/status/MqStatus'
import MqText from '../core/text/MqText'
import RunInfo from './RunInfo'
Expand All @@ -53,16 +54,23 @@ interface DispatchProps {
fetchJobTags: typeof fetchJobTags
}

interface RunsState {
page: number
}

type IProps = {
job: LineageJob
jobs: IState['jobs']
runs: Run[]
runsLoading: boolean
display: IState['display']
tabIndex: IState['lineage']['tabIndex']
jobTags: string[]
totalCount: number
runsLoading: boolean
} & DispatchProps

const PAGE_SIZE = 10

const JobDetailPage: FunctionComponent<IProps> = (props) => {
const theme = createTheme(useTheme())
const {
Expand All @@ -74,25 +82,42 @@ const JobDetailPage: FunctionComponent<IProps> = (props) => {
dialogToggle,
runs,
display,
runsLoading,
tabIndex,
setTabIndex,
jobTags,
fetchJobTags,
totalCount,
runsLoading,
} = props
const navigate = useNavigate()
const [_, setSearchParams] = useSearchParams()

const handleChange = (event: ChangeEvent, newValue: number) => {
setTabIndex(newValue)
}

const [state, setState] = React.useState<RunsState>({
page: 0,
})

const handleClickPage = (direction: 'prev' | 'next') => {
const directionPage = direction === 'next' ? state.page + 1 : state.page - 1
// reset page scroll
window.scrollTo(0, 0)
setState({ ...state, page: directionPage })
}

const i18next = require('i18next')

// return only latest row
useEffect(() => {
fetchRuns(job.name, job.namespace)
fetchJobTags(job.namespace, job.name)
}, [job.name])
Comment on lines 90 to 95

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we be setting this to a variable unless I'm missing something to page on this?

@davidsharp7 davidsharp7 Jul 14, 2024

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's because the "runs" are used by the job detail page so the thinking being that you pull the latest 10 runs so that job details works and then control all the paging from "Runs". Think there is a similar thing with "Dataset Versions" as that's pulled in the top level component and pushed downwards.

But in terms of having the offset and limit as a var then sure no problem. I've moved some stuff around so all of the work happens in JobDetailsPage.


useEffect(() => {
fetchRuns(job.name, job.namespace, PAGE_SIZE, state.page)
}, [job.name, state.page])

useEffect(() => {
if (jobs.deletedJobName) {
navigate('/')
Expand All @@ -104,10 +129,11 @@ const JobDetailPage: FunctionComponent<IProps> = (props) => {
return () => {
resetRuns()
resetJobs()
setTabIndex(0)
}
}, [])

if (runsLoading || jobs.isLoading) {
if (jobs.isLoading || runsLoading) {
return (
<Box display={'flex'} justifyContent={'center'} mt={2}>
<CircularProgress color='primary' />
Expand Down Expand Up @@ -272,17 +298,27 @@ const JobDetailPage: FunctionComponent<IProps> = (props) => {
)
) : null}
{tabIndex === 1 && <Runs runs={runs} />}
{tabIndex === 1 && (
<MqPaging
pageSize={PAGE_SIZE}
currentPage={state.page}
totalCount={totalCount}
incrementPage={() => handleClickPage('next')}
decrementPage={() => handleClickPage('prev')}
/>
)}
</Box>
)
}

const mapStateToProps = (state: IState) => ({
runs: state.runs.result,
runsLoading: state.runs.isLoading,
display: state.display,
jobs: state.jobs,
tabIndex: state.lineage.tabIndex,
jobTags: state.jobs.jobTags,
totalCount: state.runs.totalCount,
runsLoading: state.runs.isLoading,
})

const mapDispatchToProps = (dispatch: Redux.Dispatch) =>
Expand Down
4 changes: 3 additions & 1 deletion web/src/components/jobs/JobTags.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,9 @@ const JobTags: React.FC<IProps> = (props) => {
<MQText label sx={{ fontSize: '1.25rem' }} bottomMargin>
Select a Tag to change
</MQText>
<MQText label sx={{ fontSize: '0.85rem' }}>Tag</MQText>
<MQText label sx={{ fontSize: '0.85rem' }}>
Tag
</MQText>
<Autocomplete
options={tagData.map((option) => option.name)}
autoSelect
Expand Down
2 changes: 1 addition & 1 deletion web/src/components/jobs/Runs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ const Runs: FunctionComponent<RunsProps> = (props) => {
<TableCell align='left'>{formatUpdatedAt(run.createdAt)}</TableCell>
<TableCell align='left'>{formatUpdatedAt(run.startedAt)}</TableCell>
<TableCell align='left'>N/A</TableCell>
<TableCell align='left'>{stopWatchDuration(run.durationMs)}</TableCell>
<TableCell align='left'>{run.state === 'RUNNING' || run.state === 'NEW'? 'N/A': stopWatchDuration(run.durationMs)} </TableCell>
</TableRow>
)
})}
Expand Down
7 changes: 5 additions & 2 deletions web/src/store/actionCreators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,19 +296,22 @@ export const deleteJobSuccess = (jobName: string) => ({
},
})

export const fetchRuns = (jobName: string, namespace: string) => ({
export const fetchRuns = (jobName: string, namespace: string, limit: number, offset: number) => ({
type: actionTypes.FETCH_RUNS,
payload: {
jobName,
namespace,
limit,
offset,
},
})

export const fetchRunsSuccess = (jobName: string, jobRuns: Run[]) => ({
export const fetchRunsSuccess = (jobName: string, jobRuns: Run[], totalCount: number) => ({
type: actionTypes.FETCH_RUNS_SUCCESS,
payload: {
jobName,
runs: jobRuns,
totalCount,
},
})

Expand Down
12 changes: 9 additions & 3 deletions web/src/store/reducers/runs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { FETCH_RUNS, FETCH_RUNS_SUCCESS, RESET_RUNS } from '../actionCreators/ac
import { Run } from '../../types/api'
import { fetchRunsSuccess } from '../actionCreators'

export type IRunsState = { isLoading: boolean; result: Run[]; init: boolean }
export type IRunsState = { isLoading: boolean; result: Run[]; init: boolean; totalCount: number }

export const initialState: IRunsState = { isLoading: false, result: [], init: false }
export const initialState: IRunsState = { isLoading: false, result: [], init: false, totalCount: 0 }

type IRunsAction = ReturnType<typeof fetchRunsSuccess>

Expand All @@ -17,7 +17,13 @@ export default (state = initialState, action: IRunsAction): IRunsState => {
case FETCH_RUNS:
return { ...state, isLoading: true }
case FETCH_RUNS_SUCCESS:
return { ...state, isLoading: false, init: true, result: payload.runs }
return {
...state,
isLoading: false,
init: true,
result: payload.runs,
totalCount: payload.totalCount,
}
case RESET_RUNS:
return initialState
default:
Expand Down
7 changes: 6 additions & 1 deletion web/src/store/requests/jobs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ export const deleteJob = async (jobName: string, namespace: string) => {
return genericFetchWrapper(url, { method: 'DELETE' }, 'deleteJob')
}

export const getRuns = async (jobName: string, namespace: string, limit = 100, offset = 0) => {
export const getRuns = async (
jobName: string,
namespace: string,
limit: number,
offset: number
) => {
const url = `${API_URL}/namespaces/${encodeURIComponent(namespace)}/jobs/${encodeURIComponent(
jobName
)}/runs?limit=${limit}&offset=${offset}`
Expand Down
11 changes: 9 additions & 2 deletions web/src/store/sagas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
Jobs,
LineageGraph,
Namespaces,
Runs,
Tags,
} from '../../types/api'
import { all, put, take } from 'redux-saga/effects'
Expand Down Expand Up @@ -167,8 +168,14 @@ export function* fetchRunsSaga() {
while (true) {
try {
const { payload } = yield take(FETCH_RUNS)
const { runs } = yield call(getRuns, payload.jobName, payload.namespace)
yield put(fetchRunsSuccess(payload.jobName, runs))
const response: Runs = yield call(
getRuns,
payload.jobName,
payload.namespace,
payload.limit,
payload.offset
)
yield put(fetchRunsSuccess(payload.jobName, response.runs, response.totalCount))
} catch (e) {
yield put(applicationError('Something went wrong while fetching job runs'))
}
Expand Down
1 change: 1 addition & 0 deletions web/src/types/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ export type JobType = 'BATCH' | 'STREAM' | 'SERVICE'

export interface Runs {
runs: Run[]
totalCount: number
}

export interface Run {
Expand Down