diff --git a/packages/canyon-platform/app/api/project/[id]/chart/route.ts b/packages/canyon-platform/app/api/project/[id]/chart/route.ts new file mode 100644 index 00000000..0f3d2dbe --- /dev/null +++ b/packages/canyon-platform/app/api/project/[id]/chart/route.ts @@ -0,0 +1,41 @@ +import prisma from "@/lib/prisma"; + +import { NextRequest } from "next/server"; +import { percent } from "canyon-data"; + +export async function GET(request: NextRequest) { + const { pathname } = request.nextUrl; + const id = pathname.split("/")[3]; + + const coverages = await prisma.coverage + .findMany({ + where: { + projectID: id, + covType: "all", + }, + select: { + sha: true, + projectID: true, + statementsCovered: true, + statementsTotal: true, + updatedAt: true, + branch: true, + }, + orderBy: { + updatedAt: "desc", + }, + }) + .then((r) => { + return r.map((item) => { + return { + sha: item.sha, + projectID: item.projectID, + statements: percent(item.statementsCovered, item.statementsTotal), + branch: item.branch, + lastReportTime: item.updatedAt, + }; + }); + }); + + return Response.json(coverages); +} diff --git a/packages/canyon-platform/app/api/project/[id]/compartment/route.ts b/packages/canyon-platform/app/api/project/[id]/compartment/route.ts new file mode 100644 index 00000000..119e00de --- /dev/null +++ b/packages/canyon-platform/app/api/project/[id]/compartment/route.ts @@ -0,0 +1,69 @@ +import prisma from "@/lib/prisma"; + +import { NextRequest } from "next/server"; +import { percent } from "canyon-data"; +import dayjs from "dayjs"; + +export async function GET(request: NextRequest) { + const { pathname } = request.nextUrl; + + const id = pathname.split("/")[3]; + + const coverages = await prisma.coverage + .findMany({ + where: { + projectID: id, + }, + select: { + sha: true, + projectID: true, + statementsCovered: true, + statementsTotal: true, + updatedAt: true, + branch: true, + covType: true, + }, + orderBy: { + updatedAt: "desc", + }, + }) + .then((r) => { + return r.map((item) => { + return { + sha: item.sha, + projectID: item.projectID, + statements: percent(item.statementsCovered, item.statementsTotal), + branch: item.branch, + lastReportTime: item.updatedAt, + covType: item.covType, + }; + }); + }); + + const maxCoverage = Math.max( + ...coverages + .filter((item) => item.covType === "all") + .map((item) => item.statements), + ); + const totalTimes = coverages.filter((item) => item.covType === "all").length; + + const latest = coverages.filter((item) => item.covType === "agg")[0]; + return Response.json([ + { + label: "projects.total_times", + value: String(totalTimes), + }, + { + label: "projects.max_coverage", + value: maxCoverage + "%", + }, + { + label: "projects.latest_report_time", + value: dayjs(latest.lastReportTime).format("MM-DD HH:mm"), + }, + { + label: "projects.latest_report_coverage", + value: latest.statements + "%", + }, + ]); +} diff --git a/packages/canyon-platform/app/api/project/[id]/record/route.ts b/packages/canyon-platform/app/api/project/[id]/record/route.ts index a8321328..88282183 100644 --- a/packages/canyon-platform/app/api/project/[id]/record/route.ts +++ b/packages/canyon-platform/app/api/project/[id]/record/route.ts @@ -2,19 +2,61 @@ import prisma from "@/lib/prisma"; import { NextRequest } from "next/server"; import { percent } from "canyon-data"; +import axios from "axios"; + +async function getCommits(projectID: string, shas: string[]) { + const [provider, id, slug] = projectID.split("-"); + const gitProvider = await prisma.gitProvider.findFirst({ + where: { + id: provider, + }, + }); + + if (gitProvider) { + // TODO: 从gitlab获取文件内容,还去要判断provider type,例如github。 + // 判断provider类型,例如gitlab、github、gitea + + switch (gitProvider.type) { + case "gitlab": + const { url: gitlabUrl, privateToken: gitlabPrivateToken } = + gitProvider; + return Promise.all( + shas.map((sha) => + axios + .get( + `${gitlabUrl}/api/v4/projects/${id}/repository/commits/${sha}`, + { + params: { + ref: sha, + }, + headers: { + "private-token": gitlabPrivateToken, + }, + }, + ) + .then(({ data }) => data), + ), + ); + case "github": + return []; + case "gitea": + return []; + default: + return []; + } + } else { + return []; + } +} export async function GET(request: NextRequest) { const { pathname } = request.nextUrl; - // const provider = pathname.split("/")[2]; const id = pathname.split("/")[3]; - console.log("id", id); - // const slug = pathname.split("/")[4]; - // const projectID = `${provider}-${id}-${slug}`; + const coverages = await prisma.coverage .findMany({ where: { projectID: id, - covType: "all", }, select: { sha: true, @@ -23,6 +65,8 @@ export async function GET(request: NextRequest) { statementsTotal: true, updatedAt: true, branch: true, + covType: true, + reportID: true, }, orderBy: { updatedAt: "desc", @@ -31,16 +75,42 @@ export async function GET(request: NextRequest) { .then((r) => { return r.map((item) => { return { - sha: item.sha, - projectID: item.projectID, - times: 0, + ...item, statements: percent(item.statementsCovered, item.statementsTotal), - message: "message", - branch: item.branch, lastReportTime: item.updatedAt, + // message: "message", }; }); }); - return Response.json(coverages); + const rows = coverages + .filter((item) => item.covType === "all") + .map((item) => ({ + ...item, + aggs: [], + })); + + // 获取gitlab commit message + + const shas = await getCommits( + id, + rows.map((item) => item.sha), + ); + + const aggs = coverages.filter((item) => item.covType === "agg"); + + for (let i = 0; i < aggs.length; i++) { + const row = rows.find((item) => item.sha === aggs[i].sha); + if (row) { + row.aggs.push(aggs[i]); + } + } + + return Response.json( + rows.map((item) => ({ + ...item, + times: item.aggs.length, + message: shas.find((sha) => sha.id === item.sha)?.message, + })), + ); } diff --git a/packages/canyon-platform/app/page.tsx b/packages/canyon-platform/app/page.tsx index 5ba33e6d..194e2d95 100755 --- a/packages/canyon-platform/app/page.tsx +++ b/packages/canyon-platform/app/page.tsx @@ -1,10 +1,4 @@ -// import Image from "next/image"; -import { Button } from "antd"; - -export default function Home() { - return ( -
- -
- ); +import { redirect } from "next/navigation"; +export default function Page() { + redirect("/projects"); } diff --git a/packages/canyon-platform/app/projects/[provider]/[id]/[slug]/page.tsx b/packages/canyon-platform/app/projects/[provider]/[id]/[slug]/page.tsx index 36e3bdb0..ec568c37 100644 --- a/packages/canyon-platform/app/projects/[provider]/[id]/[slug]/page.tsx +++ b/packages/canyon-platform/app/projects/[provider]/[id]/[slug]/page.tsx @@ -10,26 +10,9 @@ import { useParams } from "next/navigation"; import WithTheme from "@/theme"; import { EditOutlined } from "@ant-design/icons"; import dayjs from "dayjs"; +import { useTranslations } from "next-intl"; const { Title, Text } = Typography; -const getProjectCompartmentData = [ - { - label: "name", - value: "100", - }, - { - label: "name", - value: "100", - }, - { - label: "name", - value: "100", - }, - { - label: "name", - value: "100", - }, -]; const fetcher = ({ url, params }: { url: string; params: any }) => axios @@ -39,11 +22,38 @@ const fetcher = ({ url, params }: { url: string; params: any }) => .then((res) => res.data); const { useToken } = theme; -const t = (msg) => msg; - -// http://localhost:3000/projects/tripgl/62940/auto const ProjectOverviewPage = () => { + const t = useTranslations(); + const { id, provider, slug } = useParams(); + + const { data: record } = useSWR( + { + url: `/api/project/${provider}-${id}-${slug}/record`, + }, + fetcher, + ); + + const { data: chartData } = useSWR( + { + url: `/api/project/${provider}-${id}-${slug}/chart`, + }, + fetcher, + ); + + const { data: compartmentData } = useSWR( + { + url: `/api/project/${provider}-${id}-${slug}/compartment`, + }, + fetcher, + ); + + const { data: projectData, isLoading } = useSWR( + { + url: `/api/project/${provider}-${id}-${slug}`, + }, + fetcher, + ); const option = { backgroundColor: "transparent", grid: { @@ -61,36 +71,17 @@ const ProjectOverviewPage = () => { }, xAxis: { type: "category", - data: [], + data: (chartData || []).map(({ sha }) => sha.slice(0, 7)) || [], }, yAxis: { type: "value", }, - series: [t("projects.statements"), t("projects.newlines")].map( - (_, index) => ({ - name: _, - data: [], - type: "line", - }), - ), + series: [t("projects.statements")].map((_, index) => ({ + name: _, + data: (chartData || []).map(({ statements }) => statements) || [], + type: "line", + })), }; - const { filepath, id, sha, provider, slug } = useParams(); // 获取动态路由参数 - // console.log(id) - // 非常重要的一步,获取整体覆盖率数据 - const { data: record } = useSWR( - { - url: `/api/project/${provider}-${id}-${slug}/record`, - }, - fetcher, - ); - - const { data: projectData,isLoading } = useSWR( - { - url: `/api/project/${provider}-${id}-${slug}`, - }, - fetcher, - ); - const { token } = useToken(); const columns: ColumnsType = [ { @@ -140,7 +131,9 @@ const ProjectOverviewPage = () => { // width: '148px', render(_, { sha }) { return ( - {_}% + + {_}% + ); }, }, @@ -157,24 +150,24 @@ const ProjectOverviewPage = () => { return {dayjs(_).format("MM-DD HH:mm")}; }, }, - { - title: "Option", - width: "125px", - render(_): JSX.Element { - return ( -
- { - // setOpen(true); - // setSha(_.sha); - }} - > - {"Details"} - -
- ); - }, - }, + // { + // title: "Option", + // width: "125px", + // render(_): JSX.Element { + // return ( + //
+ // { + // // setOpen(true); + // // setSha(_.sha); + // }} + // > + // {"Details"} + // + //
+ // ); + // }, + // }, ]; return ( @@ -238,7 +231,7 @@ const ProjectOverviewPage = () => {
- {(getProjectCompartmentData || []).map((item, index) => { + {(compartmentData || []).map((item, index) => { return (
{ }} key={index} > - {item.label} - {item.value} + {/*{t(item.label)}*/} + {/*{item.value}*/} + {t(item.label)} + + {item.value} +
); })} @@ -268,16 +270,16 @@ const ProjectOverviewPage = () => { }} >
- - {"Trends in coverage"} - - + {t("projects.trends_in_coverage")} + + - {"Tooltip"} - + {t("projects.trends.tooltip")} +
{ style={{ width: "600px" }} /> - {"Only Default Branch"}: + {"Only Default Branch"}: {/* { }} /> - {/*默太狂就共用一个*/} {/**/}