fennec-fe/src/pipeline-tasks/pipeline-task-detail.tsx

191 lines
5.3 KiB
TypeScript

import { gql, useQuery, useSubscription } from "@apollo/client";
import { LinearProgress, makeStyles, Typography } from "@material-ui/core";
import { format } from "date-fns";
import React, { FC, useState } from "react";
import { ErrorPage } from "../commons/fallbacks/error-page";
import {
PipelineTask,
PipelineTaskEvent,
PipelineTaskLogs,
TaskStatuses,
} from "../generated/graphql";
import { PIPELINE_TASK_EVENT } from "./subscriptions";
import { clone, find, propEq } from "ramda";
import { PipelineUnits } from '../generated/graphql';
interface Props {
taskId: string;
}
const PIPELINE_TASK = gql`
query FindPipelineTask($taskId: String!) {
pipelineTask(id: $taskId) {
id
units
commit
status
startedAt
endedAt
logs {
unit
logs
status
startedAt
endedAt
}
}
}
`;
const useStyles = makeStyles((theme) => ({
root: {},
groupTitle: {
backgroundColor: "white",
fontWeight: 500,
borderTop: "1px solid #eee",
fontSize: "16px",
padding: "12px",
marginLeft: "1px",
},
logText: {
fontFamily: 'Menlo, Monaco, "Courier New", monospace',
whiteSpace: "pre-wrap",
border: "none",
margin: "6px 12px",
display: "block",
},
}));
export const PipelineTaskDetail: FC<Props> = ({ taskId }) => {
const [taskEvents, setTaskEvents] = useState(
() => new Array<PipelineTaskEvent>()
);
const { data, loading, error, client } = useQuery<{
pipelineTask: PipelineTask;
}>(PIPELINE_TASK, {
variables: { taskId },
});
const task = data?.pipelineTask;
useSubscription<{ pipelineTaskEvent: PipelineTaskEvent }>(
PIPELINE_TASK_EVENT,
{
variables: { taskId },
onSubscriptionData({ subscriptionData: { data } }) {
const event = data?.pipelineTaskEvent;
console.log(event);
if (event && task) {
setTaskEvents((prev) => [...prev, event]);
if (event.unit) {
// event of running scripts
client.cache.modify({
id: client.cache.identify(task!),
fields: {
logs(before: PipelineTaskLogs[]) {
before = clone(before);
let l = find(propEq("unit", event.unit), before);
if (l) {
l.logs += event.message;
} else {
l = {
unit: event.unit!,
logs: event.message,
status: event.status,
startedAt: event.emittedAt,
endedAt: null,
__typename: "PipelineTaskLogs",
};
before.push(l);
}
if (event.status === TaskStatuses.Working) {
l.startedAt || (l.startedAt = event.emittedAt);
}
if (
[TaskStatuses.Failed, TaskStatuses.Success].includes(
event.status
)
) {
l.startedAt && (l.startedAt = event.emittedAt);
l.endedAt = event.emittedAt;
}
l.status = event.status;
return before;
},
},
});
} else {
// event of task status change
client.cache.modify({
id: client.cache.identify(task!),
fields: {
status() {
return event.status;
},
startedAt(before) {
if (event.status === TaskStatuses.Working) {
return event.emittedAt;
} else {
return before;
}
},
endedAt(before) {
if (
[TaskStatuses.Success, TaskStatuses.Failed].includes(
event.status
)
) {
return event.emittedAt;
} else {
return before;
}
},
},
});
}
}
},
}
);
const classes = useStyles();
if (error) {
return <ErrorPage>{error.message}</ErrorPage>;
}
if (loading) {
return <LinearProgress color="secondary" />;
}
return (
<div className={classes.root}>
<Typography variant="h4" component="h2">
{taskId} detail
</Typography>
<Typography variant="h5" component="h3">
{task?.status}
</Typography>
<Typography variant="h5" component="h3">
{task?.startedAt && format(task?.startedAt, "yyyy-MM-dd HH:mm:ss.SSS")}-
{task?.endedAt && format(task?.endedAt, "yyyy-MM-dd HH:mm:ss.SSS")}
</Typography>
{task?.logs.map((logs) => (
<LogGroup key={logs.unit} logs={logs} />
))}
</div>
);
};
const LogGroup: FC<{ logs: PipelineTaskLogs }> = ({ logs }) => {
const classes = useStyles();
return (
<div>
<div className={classes.groupTitle}>
{logs.unit}{" "}
{logs.startedAt && format(logs.startedAt, "yyyy-MM-dd HH:mm:ss")}{" "}
{logs.endedAt && format(logs.endedAt, "yyyy-MM-dd HH:mm:ss")} {logs.status}
</div>
<code className={classes.logText}>{logs.logs}</code>
</div>
);
};