Skip to content

Commit

Permalink
Show Bodhi updates in jobs and pipelines
Browse files Browse the repository at this point in the history
Fixes #215
  • Loading branch information
lbarcziova committed Jan 22, 2024
1 parent d593930 commit 48bd5d0
Show file tree
Hide file tree
Showing 6 changed files with 320 additions and 2 deletions.
145 changes: 145 additions & 0 deletions frontend/src/app/Jobs/BodhiUpdatesTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import React, { useMemo } from "react";

import { TableVariant, cellWidth, IRow } from "@patternfly/react-table";
import {
Table,
TableHeader,
TableBody,
} from "@patternfly/react-table/deprecated";

import { Button } from "@patternfly/react-core";
import { TriggerLink } from "../Trigger/TriggerLink";
import { ErrorConnection } from "../Errors/ErrorConnection";
import { Preloader } from "../Preloader/Preloader";
import { ForgeIcon } from "../Forge/ForgeIcon";
import { StatusLabel } from "../StatusLabel/StatusLabel";
import { Timestamp } from "../utils/Timestamp";
import { useInfiniteQuery } from "@tanstack/react-query";

export interface BodhiUpdate {
packit_id: number;
status: string;
alias: string | null;
web_url: string | null;
branch: string;
submitted_time: number;
update_creation_time: number | null;
pr_id: number | null;
branch_name: string | null;
release: string | null;
project_url: string;
repo_namespace: string;
repo_name: string;
}
const BodhiUpdatesTable = () => {
// Headings
const columns = [
{
title: <span className="pf-v5-u-screen-reader">Forge</span>,
}, // space for forge icon
{ title: "Trigger", transforms: [cellWidth(35)] },
{ title: "Branch", transforms: [cellWidth(20)] },
{ title: "Time Processed", transforms: [cellWidth(20)] },
{ title: "Bodhi Update", transforms: [cellWidth(20)] },
];

// Fetch data from dashboard backend (or if we want, directly from the API)
const fetchData = ({ pageParam = 1 }) =>
fetch(
`${
import.meta.env.VITE_API_URL
}/bodhi-updates?page=${pageParam}&per_page=20`,
)
.then((response) => response.json())
.then((data) => jsonToRow(data));

const { isInitialLoading, isError, fetchNextPage, data, isFetching } =
useInfiniteQuery(["bodhi"], fetchData, {
getNextPageParam: (_, allPages) => allPages.length + 1,
});

// Convert fetched json into row format that the table can read
function jsonToRow(bodhi_updates: BodhiUpdate[]) {
let rowsList: IRow[] = [];

bodhi_updates.forEach((bodhi_update) => {
let singleRow = {
cells: [
{
title: <ForgeIcon url={bodhi_update.project_url} />,
},
{
title: (
<strong>
<TriggerLink builds={bodhi_update} />
</strong>
),
},
{
title: (
<StatusLabel
target={bodhi_update.branch}
status={bodhi_update.status}
link={`/results/bodhi-updates/${bodhi_update.packit_id}`}
/>
),
},
{
title: <Timestamp stamp={bodhi_update.submitted_time} />,
},
{
title: (
<strong>
<a href={bodhi_update.web_url} target="_blank" rel="noreferrer">
{bodhi_update.alias}
</a>
</strong>
),
},
],
};
rowsList.push(singleRow);
});
return rowsList;
}

// Create a memoization of all the data when we flatten it out. Ideally one should render all the pages separately so that rendering will be done faster
const rows = useMemo(() => (data ? data.pages.flat() : []), [data]);

// If backend API is down
if (isError) {
return <ErrorConnection />;
}

// Show preloader if waiting for API data
// TODO(SpyTec): Replace with skeleton loader, we know the data will look like
if (isInitialLoading) {
return <Preloader />;
}

return (
<div>
<Table
aria-label="Bodhi updates"
variant={TableVariant.compact}
cells={columns}
rows={rows}
>
<TableHeader />
<TableBody />
</Table>
<center>
<br />
<Button
variant="control"
onClick={() => fetchNextPage()}
isAriaDisabled={isFetching}
>
{isFetching ? "Fetching data" : "Load more"}
</Button>
</center>
</div>
);
};

export { BodhiUpdatesTable };
3 changes: 3 additions & 0 deletions frontend/src/app/Jobs/Jobs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ const Jobs = () => {
Downstream Koji Builds
</NavLink>
</NavItem>
<NavItem isActive={currentMatch?.id === "bodhi-updates"}>
<NavLink to={"bodhi-updates"}>Bodhi Updates</NavLink>
</NavItem>
</NavList>
</Nav>
</PageNavigation>
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/app/Pipelines/PipelinesTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@ const PipelinesTable = () => {
statusClass={SyncReleaseTargetStatusLabel}
entries={run.pull_from_upstream}
/>
<Statuses
name={"Bodhi Updates"}
route={"bodhi-updates"}
statusClass={StatusLabel}
entries={run.bodhi_update}
/>
</>
),
},
Expand Down
150 changes: 150 additions & 0 deletions frontend/src/app/Results/ResultsPageBodhiUpdate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import React from "react";
import {
PageSection,
Card,
CardBody,
PageSectionVariants,
TextContent,
Text,
Title,
Label,
DescriptionList,
DescriptionListDescription,
DescriptionListGroup,
DescriptionListTerm,
} from "@patternfly/react-core";
import { TableHeader, TableBody } from "@patternfly/react-table/deprecated";
import { Table, Tbody, Td, Th, Thead, Tr } from "@patternfly/react-table";
import { ErrorConnection } from "../Errors/ErrorConnection";
import { Preloader } from "../Preloader/Preloader";
import { TriggerLink } from "../Trigger/TriggerLink";
import { StatusLabel } from "../StatusLabel/StatusLabel";
import { Timestamp } from "../utils/Timestamp";
import { useParams } from "react-router-dom";
import { useTitle } from "../utils/useTitle";
import { getCommitLink } from "../utils/forgeUrls";
import { useQuery } from "@tanstack/react-query";
import { SHACopy } from "../utils/SHACopy";

interface BodhiUpdate {
packit_id: number;
status: string;
alias: string | null;
web_url: string | null;
branch: string;
submitted_time: number;
update_creation_time: number | null;
pr_id: number | null;
branch_name: string | null;
release: string | null;
project_url: string;
repo_namespace: string;
repo_name: string;
}

const fetchBodhiUpdates = (url: string) =>
fetch(url).then((response) => {
if (!response.ok && response.status !== 404) {
throw Promise.reject(response);
}
return response.json();
});

const ResultsPageBodhiUpdate = () => {
useTitle("Bodhi Updates");
let { id } = useParams();

const URL = `${import.meta.env.VITE_API_URL}/bodhi-updates/${id}`;
const { data, isError, isInitialLoading } = useQuery<
BodhiUpdate | { error: string }
>([URL], () => fetchBodhiUpdates(URL));

// If backend API is down
if (isError) {
return <ErrorConnection />;
}

// Show preloader if waiting for API data
if (isInitialLoading || data === undefined) {
return <Preloader />;
}

if ("error" in data) {
return (
<PageSection>
<Card>
<CardBody>
<Title headingLevel="h1" size="lg">
Not Found.
</Title>
</CardBody>
</Card>
</PageSection>
);
}

return (
<>
<PageSection variant={PageSectionVariants.light}>
<TextContent>
<Text component="h1">Bodhi Update Results</Text>
<Text component="p">
<strong>
<TriggerLink builds={data} />
<SHACopy git_repo={data.git_repo} commit_sha={data.commit_sha} />
</strong>
<br />
</Text>
</TextContent>
</PageSection>

<PageSection>
<Card>
<CardBody>
<DescriptionList
columnModifier={{
default: "1Col",
sm: "2Col",
}}
>
<DescriptionListGroup>
<DescriptionListTerm>Status</DescriptionListTerm>
<DescriptionListDescription>
<StatusLabel
target={data.branch}
status={data.status}
link={data.web_url}
/>{" "}
</DescriptionListDescription>
<DescriptionListTerm>Alias</DescriptionListTerm>
<DescriptionListDescription>
{" "}
{data.alias !== null ? data.alias : <span>not provided</span>}
</DescriptionListDescription>
<DescriptionListTerm>Koji NVR</DescriptionListTerm>
<DescriptionListDescription>
{" "}
{data.koji_nvr}
</DescriptionListDescription>
</DescriptionListGroup>
<DescriptionListGroup>
<DescriptionListTerm>
Update Processing Time
</DescriptionListTerm>
<DescriptionListDescription>
<Timestamp stamp={data.submitted_time} verbose={true} />
</DescriptionListDescription>
<DescriptionListTerm>Update Creation Time</DescriptionListTerm>
<DescriptionListDescription>
<Timestamp stamp={data.update_creation_time} verbose={true} />
</DescriptionListDescription>
</DescriptionListGroup>
</DescriptionList>
</CardBody>
</Card>
</PageSection>
</>
);
};

export { ResultsPageBodhiUpdate };
14 changes: 14 additions & 0 deletions frontend/src/app/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ import { ResultsPageKoji } from "./Results/ResultsPageKoji";
import { ResultsPageSyncReleaseRuns } from "./Results/ResultsPageSyncReleaseRuns";
import { ResultsPageSRPM } from "./Results/ResultsPageSRPM";
import { ResultsPageTestingFarm } from "./Results/ResultsPageTestingFarm";
import { ResultsPageBodhiUpdate } from "./Results/ResultsPageBodhiUpdate";
import { CoprBuildsTable } from "./Jobs/CoprBuildsTable";
import { KojiBuildsTable } from "./Jobs/KojiBuildsTable";
import { SyncReleaseTable } from "./Jobs/SyncReleaseStatuses";
import { SRPMBuildsTable } from "./Jobs/SRPMBuildsTable";
import { TestingFarmResultsTable } from "./Jobs/TestingFarmResultsTable";
import { BodhiUpdatesTable } from "./Jobs/BodhiUpdatesTable";
import { Usage } from "./Usage/Usage";
import { ErrorApp } from "./Errors/ErrorApp";

Expand Down Expand Up @@ -113,6 +115,14 @@ const routes: RouteObject[] = [
label: "Downstream (production) Koji builds",
},
},
{
element: <BodhiUpdatesTable />,
id: "bodhi-updates",
path: "bodhi-updates",
handle: {
label: "Bodhi Updates",
},
},
],
},
{
Expand Down Expand Up @@ -159,6 +169,10 @@ const routes: RouteObject[] = [
path: "/results/pull-from-upstream/:id",
element: <ResultsPageSyncReleaseRuns job="pull-from-upstream" />,
},
{
path: "/results/bodhi-updates/:id",
element: <ResultsPageBodhiUpdate />,
},
{
element: <Usage />,
path: "/usage",
Expand Down
4 changes: 2 additions & 2 deletions frontend/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve"
"jsx": "preserve",
},
"include": ["src"],
"exclude": ["node_modules"]
"exclude": ["node_modules"],
}

0 comments on commit 48bd5d0

Please sign in to comment.