Skip to content

Commit

Permalink
add job deletion functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
onursumer committed Dec 31, 2024
1 parent ddd09bc commit 4721108
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 6 deletions.
16 changes: 16 additions & 0 deletions apps/cohort-request-api/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,22 @@ app.get(`${API_ROOT}/job-detailed`, async (req, res) => {
res.send(chain([response]).flatten().compact().value());
});

app.get(`${API_ROOT}/terminate-job`, async (req, res) => {
const jobId = req.query['jobId'] as string;
const response = isEmpty(jobId)
? undefined
: await requestTracker.terminateJobById(jobId).catch(() => undefined);
res.send(chain([response]).flatten().compact().value());
});

app.get(`${API_ROOT}/recover-job`, async (req, res) => {
const jobId = req.query['jobId'] as string;
const response = isEmpty(jobId)
? undefined
: await requestTracker.recoverJobById(jobId).catch(() => undefined);
res.send(chain([response]).flatten().compact().value());
});

app.listen(port, host, () => {
console.log(`[ ready ] http://${host}:${port}`);
});
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import React, { useEffect, useState } from 'react';
import { Container } from 'react-bootstrap';
import React, { useCallback, useEffect, useState } from 'react';
import { Button, Container } from 'react-bootstrap';
import { useSearchParams } from 'react-router-dom';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/styles/ag-grid.css'; // Core CSS
import 'ag-grid-community/styles/ag-theme-quartz.css'; // Theme
import { faTrashCan } from '@fortawesome/free-solid-svg-icons';
import {
EnhancedJob,
fetchJobsDetailed,
terminateJob,
} from '@cbioportal-cohort-request/cohort-request-utils';
import IconWithTooltip from '../icon-with-tooltip/IconWithTooltip';
import {
dateFormatter,
DefaultColumnDefinition,
Expand All @@ -20,14 +24,51 @@ import {
export interface RequestTrackerProps {}

export function RequestTracker(props: RequestTrackerProps) {
const [searchParams] = useSearchParams();
const handleJobFetch = useCallback(
(result: { data: EnhancedJob[] }) => {
// filter out terminated jobs
const data = searchParams.get('debug')
? result.data
: result.data.filter((d) => !d.terminationTimestamp);
setRowData(data);
},
[searchParams]
);

useEffect(() => {
fetchJobsDetailed().then((result) => {
setRowData(result.data);
});
}, []);
fetchJobsDetailed().then(handleJobFetch);
}, [handleJobFetch]);

const [rowData, setRowData] = useState<EnhancedJob[]>([]);

const DeleteJob = (props: { value?: number; data?: EnhancedJob }) => {
const handleDelete = () => {
if (props?.data?.jobId) {
terminateJob({ jobId: props?.data?.jobId }).then((result) => {
if (result.data.length > 0) {
// fetch jobs again to refresh the table (to remove deleted jobs)
fetchJobsDetailed().then(handleJobFetch);
}
});
}
};

// TODO prompt "Are you sure?" before deleting
return (
<Button
disabled={!!props?.data?.terminationTimestamp}
variant="danger"
onClick={handleDelete}
>
<IconWithTooltip
icon={faTrashCan}
tooltipContent={<>Delete Job {props?.data?.jobId.substring(0, 7)}</>}
/>
</Button>
);
};

const colDefs = [
{ field: 'jobId', cellRenderer: JobIdColumn },
DefaultColumnDefinition.Status,
Expand All @@ -44,6 +85,11 @@ export function RequestTracker(props: RequestTrackerProps) {
DefaultColumnDefinition.Users,
DefaultColumnDefinition.AdditionalData,
{ field: 'events', cellRenderer: EventColumn },
{
headerName: 'Delete',
field: 'terminationTimestamp',
cellRenderer: DeleteJob,
},
];

return (
Expand Down
34 changes: 34 additions & 0 deletions libs/cohort-request-node-utils/src/lib/cohort-request-tracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,40 @@ export class CohortRequestTracker {
return fetchJobById(jobId, this.jobDB);
}

public terminateJobById(jobId: string): Promise<Job | undefined> {
return this.fetchJobById(jobId).then((job) => {
// if already terminated just return the job
if (job.terminationTimestamp) {
return job;
}
// if not terminated yet, terminate the job and update the DB
job.terminationTimestamp = Date.now();
return insertJob(job, this.jobDB)
.then(() => job)
.catch((error) => {
console.log(JSON.stringify(error));
return undefined;
});
});
}

public recoverJobById(jobId: string): Promise<Job | undefined> {
return this.fetchJobById(jobId).then((job) => {
// if not terminated yet just return the job
if (!job.terminationTimestamp) {
return job;
}
// remove termination stamp so that the job can be recovered
job.terminationTimestamp = undefined;
return insertJob(job, this.jobDB)
.then(() => job)
.catch((error) => {
console.log(JSON.stringify(error));
return undefined;
});
});
}

public fetchAllJobsDetailed(): Promise<EnhancedJob[]> {
return Promise.all([this.fetchAllEvents(), this.fetchAllJobs()]).then(
(response) => {
Expand Down
12 changes: 12 additions & 0 deletions libs/cohort-request-utils/src/lib/cohort-request-api-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,15 @@ export async function fetchJobsDetailed(
): Promise<AxiosResponse<EnhancedJob[]>> {
return axios.get('/api/job-detailed', { params });
}

export async function terminateJob(
params?: Dictionary<string>
): Promise<AxiosResponse<EnhancedJob[]>> {
return axios.get('/api/terminate-job', { params });
}

export async function recoverJob(
params?: Dictionary<string>
): Promise<AxiosResponse<EnhancedJob[]>> {
return axios.get('/api/recover-job', { params });
}
1 change: 1 addition & 0 deletions libs/cohort-request-utils/src/lib/cohort-request-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export interface Job {
filename: string;
size: number;
}[];
terminationTimestamp?: number;
}

export interface Event {
Expand Down

0 comments on commit 4721108

Please sign in to comment.