UNPKG

issue-status

Version:

A flexible, modern and blazingly fast ☄️ status page

138 lines (119 loc) 3.61 kB
import dayjs from "dayjs"; import type { ComponentType, Provider } from "../api/types"; import { Gitlab, type IssueSchemaWithBasicLabels } from "@gitbeaker/rest"; import { cached } from "./shared"; const extractStatusFromLabels = (labels: string[]): ComponentType["status"] => { const statusMap = new Map<string, ComponentType["status"]>([ ["operational", "operational"], ["degraded performance", "degradedPerformance"], ["partial outage", "partialOutage"], ["major outage", "majorOutage"], ["unknown", "unknown"], ]); return labels.reduce<ComponentType["status"]>((acc, current) => { if (acc !== "unknown") { return acc; } acc = statusMap.get(current) ?? "unknown"; return acc; }, "unknown"); }; const buildComponentHierarchy = ( issues: IssueSchemaWithBasicLabels[] ): ComponentType[] => { // parents need to be pushed in the reduce before the child const sortedIssues = [...issues].sort((a, b) => a.title.localeCompare(b.title) ); return sortedIssues.reduce<ComponentType[]>( (components, { title, id, labels }) => { const status = extractStatusFromLabels(labels); const separator = " > "; if (!title.includes(separator)) { components.push({ id: id.toString(), name: title, status, }); return components; } const [parentName, childName] = title.split(separator); const parent = components.find((c) => c.name === parentName.trim()); if (parent) { parent.children = parent.children || []; parent.children.push({ id: id.toString(), name: childName.trim(), status, }); } return components; }, [] ); }; /** * GitLab provider which uses GitLab Issues with specific labels as the data source. * * The provider respects GitLab's API rate limits and therefore responses are cached in the browser for 10 minutes. * * https://docs.gitlab.com/ee/user/gitlab_com/index.html#gitlabcom-specific-rate-limits */ export const gitlab = ({ projectId, host, }: { projectId: string; host?: string; }): Provider => { const gitlab = new Gitlab({ host: host ?? "https://gitlab.com", }); const getIncidents = async (state: "opened" | "closed") => { const data = await cached( `gitlab:${projectId}:${state}Incidents`, async () => { const issues = await gitlab.Issues.all({ projectId, labels: "issue status,incident", state, updatedAfter: state === "opened" ? undefined : dayjs().subtract(14, "days").toISOString(), }); return issues; }, 10 ); return data.map((issue) => { const isScheduled = issue.labels.includes("maintenance"); return { id: issue.id.toString(), title: issue.title, description: issue.description, createdAt: issue.created_at, scheduled: isScheduled, active: !issue.closed_at, }; }); }; return { getComponents: async () => { const data = await cached( `gitlab:${projectId}:components`, async () => { const issues = await gitlab.Issues.all({ labels: "issue status,component", projectId: projectId, }); return issues; }, 10 ); return buildComponentHierarchy(data); }, getIncidents: async () => await getIncidents("opened"), getHistoricalIncidents: async () => await getIncidents("closed"), }; };