Skip to content
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
96 changes: 76 additions & 20 deletions frontend/src/components/jobs/result/JobInfoCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,15 @@ import { PlaybookTag } from "../../common/PlaybookTag";
import { StatusTag } from "../../common/StatusTag";
import { TLPTag } from "../../common/TLPTag";
import { JobInfoIcon } from "./JobInfoIcon";
import { JobIsRunningAlert } from "./JobIsRunningAlert";
import { JobFinalStatuses } from "../../../constants/jobConst";

export function JobInfoCard({ job }) {
const navigate = useNavigate();
// local state
const [isOpen, setIsOpen] = React.useState(false);
const [isOpenJobInfoCard, setIsOpenJobInfoCard] = React.useState(false);
const [isOpenJobWarnings, setIsOpenJobWarnings] = React.useState(false);
const [isOpenJobErrors, setIsOpenJobErrors] = React.useState(false);

return (
<div id="JobInfoCardSection">
Expand Down Expand Up @@ -92,25 +96,25 @@ export function JobInfoCard({ job }) {
<Col sm={12} md={1} className="d-flex justify-content-end">
<Button
className="bg-darker border-0"
onClick={() => setIsOpen(!isOpen)}
onClick={() => setIsOpenJobInfoCard(!isOpenJobInfoCard)}
id="JobInfoCardDropDown"
>
<ArrowToggleIcon isExpanded={isOpen} />
<ArrowToggleIcon isExpanded={isOpenJobInfoCard} />
</Button>
<UncontrolledTooltip placement="left" target="JobInfoCardDropDown">
Toggle Job Metadata
</UncontrolledTooltip>
</Col>
</Row>
</ContentSection>
<Collapse isOpen={isOpen} id="JobInfoCardCollapse">
<Collapse isOpen={isOpenJobInfoCard} id="JobInfoCardCollapse">
<ContentSection className="border-top-0 bg-body ps-0 pe-1 py-1">
<ListGroup
horizontal
className="align-items-start flex-wrap flex-lg-nowrap"
>
{[
["Status", <StatusTag status={job.status} />],
["Status", <StatusTag status={job.status} className="py-0" />],
["TLP", <TLPTag value={job.tlp} />],
["User", job.user?.username],
["MD5", job.md5],
Expand All @@ -136,7 +140,7 @@ export function JobInfoCard({ job }) {
),
],
].map(([key, value]) => (
<ListGroupItem key={key}>
<ListGroupItem className="mx-2" key={key}>
<small className="fw-bold text-light">{key}</small>
<div className="bg-dark p-1 text-light">{value}</div>
</ListGroupItem>
Expand All @@ -157,33 +161,85 @@ export function JobInfoCard({ job }) {
],
[
"Tags",
job.tags.map((tag) => (
<JobTag key={tag.label} tag={tag} className="me-2" />
)),
job.tags.length ? (
job.tags.map((tag) => (
<JobTag key={tag.label} tag={tag} className="me-2" />
))
) : (
<small className="fst-italic text-gray">None</small>
),
],
[
"Warning(s)",
<ul className="text-warning">
{job.warnings.map((error) => (
<li>{error}</li>
))}
</ul>,
<>
<div className="d-flex text-warning align-items-center justify-content-between mx-2">
{job.warnings.length} warnings
<Button
className="bg-dark border-0 p-0"
onClick={() => setIsOpenJobWarnings(!isOpenJobWarnings)}
id="JobWarningsDropDown"
>
<ArrowToggleIcon isExpanded={isOpenJobWarnings} />
</Button>
<UncontrolledTooltip
placement="left"
target="JobWarningsDropDown"
>
Toggle Job Warnings
</UncontrolledTooltip>
</div>
<Collapse isOpen={isOpenJobWarnings} id="JobWarningsCollapse">
<ul className="text-warning">
{job.warnings.map((error) => (
<li>{error}</li>
))}
</ul>
</Collapse>
</>,
],
[
"Error(s)",
<ul className="text-danger">
{job.errors.map((error) => (
<li>{error}</li>
))}
</ul>,
<>
<div className="d-flex text-danger align-items-center justify-content-between mx-2">
{job.errors.length} errors
<Button
className="bg-dark border-0 p-0"
onClick={() => setIsOpenJobErrors(!isOpenJobErrors)}
id="JobErrorsDropDown"
>
<ArrowToggleIcon isExpanded={isOpenJobErrors} />
</Button>
<UncontrolledTooltip
placement="left"
target="JobErrorsDropDown"
>
Toggle Job Errors
</UncontrolledTooltip>
</div>
<Collapse isOpen={isOpenJobErrors} id="JobErrorsCollapse">
<ul className="text-danger">
{job.errors.map((error) => (
<li>{error}</li>
))}
</ul>
</Collapse>
</>,
],
].map(([key, value]) => (
<ListGroupItem key={key}>
<ListGroupItem className="mx-2" key={key}>
<small className="fw-bold text-light">{key}</small>
<div className="bg-dark p-1">{value}</div>
</ListGroupItem>
))}
</ListGroup>
{Object.values(JobFinalStatuses).includes(job.status) && (
<div
className="my-4 d-flex justify-content-center"
style={{ width: "100%" }}
>
<JobIsRunningAlert job={job} />
</div>
)}
</ContentSection>
</Collapse>
</div>
Expand Down
96 changes: 62 additions & 34 deletions frontend/src/components/jobs/result/JobIsRunningAlert.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
/* eslint-disable id-length */
import React from "react";
import PropTypes from "prop-types";
import ReactFlow, { MarkerType } from "reactflow";
import ReactFlow, {
MarkerType,
ReactFlowProvider,
useReactFlow,
} from "reactflow";
import "reactflow/dist/style.css";
import { IconButton } from "@certego/certego-ui";

import CustomJobPipelineNode from "./CustomJobPipelineNode";
import { JobStatuses } from "../../../constants/jobConst";
import { JobStatuses, JobFinalStatuses } from "../../../constants/jobConst";
import { areYouSureConfirmDialog } from "../../common/areYouSureConfirmDialog";

import {
Expand All @@ -29,7 +33,7 @@ const defaultEdgeOptions = {
},
};

export function JobIsRunningAlert({ job }) {
function JobIsRunningFlow({ job }) {
// number of analyzers/connectors/visualizers reported (status: killed/succes/failed)
const analizersReported = reportedPluginNumber(job.analyzer_reports);
const connectorsReported = reportedPluginNumber(job.connector_reports);
Expand Down Expand Up @@ -57,10 +61,12 @@ export function JobIsRunningAlert({ job }) {
.slice(9)
.includes(job.status);

const position = { x: 450, y: 0 };

const nodes = [
{
id: `isRunningJob-analyzers`,
position: { x: 0, y: 0 },
position: { x: position.x * 0, y: position.y },
data: {
id: "step-1",
label: "ANALYZERS",
Expand All @@ -75,7 +81,7 @@ export function JobIsRunningAlert({ job }) {
},
{
id: `isRunningJob-connectors`,
position: { x: 450, y: 0 },
position: { x: position.x, y: position.y },
data: {
id: "step-2",
label: "CONNECTORS",
Expand All @@ -90,7 +96,7 @@ export function JobIsRunningAlert({ job }) {
},
{
id: `isRunningJob-pivots`,
position: { x: 900, y: 0 },
position: { x: position.x * 2, y: position.y },
data: {
id: "step-3",
label: "PIVOTS",
Expand All @@ -104,7 +110,7 @@ export function JobIsRunningAlert({ job }) {
},
{
id: `isRunningJob-visualizers`,
position: { x: 1350, y: 0 },
position: { x: position.x * 3, y: position.y },
data: {
id: "step-4",
label: "VISUALIZERS",
Expand Down Expand Up @@ -137,6 +143,36 @@ export function JobIsRunningAlert({ job }) {
},
];

const reactFlowInstance = useReactFlow();
// this is necessary to properly resize the flow in Google Chrome
React.useEffect(() => {
console.debug("JobIsRunningFlow - set fitView property");
setTimeout(() => reactFlowInstance.fitView(), 0);
});

return (
<div
id="JobPipelineFlow"
className="bg-body"
style={{ width: 2000, height: 90 }}
>
<ReactFlow
nodes={nodes}
edges={edges}
defaultEdgeOptions={defaultEdgeOptions}
defaultViewport={{ x: 0, y: 0, zoom: 1.2 }}
nodeTypes={nodeTypes}
deleteKeyCode={null}
preventScrolling={false}
zoomOnDoubleClick={false}
panOnDrag={false}
elementsSelectable={false}
/>
</div>
);
}

export function JobIsRunningAlert({ job }) {
const onKillJobBtnClick = async () => {
const sure = await areYouSureConfirmDialog(`Kill Job #${job.id}`);
if (!sure) return null;
Expand All @@ -146,34 +182,23 @@ export function JobIsRunningAlert({ job }) {

return (
<>
<div className="bg-body" style={{ width: "100vw", height: "15vh" }}>
<ReactFlow
nodes={nodes}
edges={edges}
defaultEdgeOptions={defaultEdgeOptions}
defaultViewport={{ x: 0, y: 0, zoom: 1.2 }}
nodeTypes={nodeTypes}
deleteKeyCode={null}
preventScrolling={false}
zoomOnDoubleClick={false}
panOnDrag={false}
proOptions={{ hideAttribution: true }}
fitView
/>
</div>
<ReactFlowProvider>
<JobIsRunningFlow job={job} />
</ReactFlowProvider>
<div className="d-flex-center">
{job.permissions?.kill && (
<IconButton
id="killjob-iconbutton"
Icon={killJobIcon}
size="xs"
title="Stop Job Process"
color="danger"
titlePlacement="top"
onClick={onKillJobBtnClick}
className="mt-0"
/>
)}
{job.permissions?.kill &&
!Object.values(JobFinalStatuses).includes(job.status) && (
<IconButton
id="killjob-iconbutton"
Icon={killJobIcon}
size="xs"
title="Stop Job Process"
color="danger"
titlePlacement="top"
onClick={onKillJobBtnClick}
className="mt-4"
/>
)}
</div>
</>
);
Expand All @@ -182,3 +207,6 @@ export function JobIsRunningAlert({ job }) {
JobIsRunningAlert.propTypes = {
job: PropTypes.object.isRequired,
};
JobIsRunningFlow.propTypes = {
job: PropTypes.object.isRequired,
};
5 changes: 4 additions & 1 deletion frontend/src/components/jobs/result/JobOverview.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,10 @@ export function JobOverview({
</Col>
</Row>
{isRunningJob && (
<Row>
<Row
className="my-4 d-flex justify-content-center"
style={{ width: "100%" }}
>
<JobIsRunningAlert job={job} />
</Row>
)}
Expand Down
Loading