Skip to content

Commit

Permalink
feature: use server-side rendering for incident list
Browse files Browse the repository at this point in the history
  • Loading branch information
Kiryous committed Oct 29, 2024
1 parent 0570111 commit 9a9a310
Show file tree
Hide file tree
Showing 12 changed files with 146 additions and 42 deletions.
2 changes: 2 additions & 0 deletions keep-ui/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ pids
*.pid
*.seed

!lib

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

Expand Down
50 changes: 19 additions & 31 deletions keep-ui/app/error.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,22 @@
"use client";
import Image from "next/image";
import "./error.css";
import {useEffect} from "react";
import {Title, Subtitle} from "@tremor/react";
import {Button, Text} from "@tremor/react";
import { useEffect } from "react";
import { Title, Subtitle } from "@tremor/react";
import { Button, Text } from "@tremor/react";
import { signOut } from "next-auth/react";
import { KeepApiError } from "@/shared/lib/KeepApiError";

export default function ErrorComponent({
error,
reset,
}: {
error: Error | KeepApiError
reset: () => void
error: Error | KeepApiError;
reset: () => void;
}) {
useEffect(() => {
console.error(error)
}, [error])
console.error(error);
}, [error]);

return (
<div className="error-container">
Expand All @@ -29,16 +30,16 @@ export default function ErrorComponent({
<code>
{error instanceof KeepApiError && (
<div className="mt-4">
Status Code: {error.statusCode}<br />
Status Code: {error.statusCode}
<br />
Message: {error.message}
</div>
)}
</code>
</div>
{error instanceof KeepApiError && error.proposedResolution && (<Subtitle className="mt-4">
{error.proposedResolution}
</Subtitle>)
}
{error instanceof KeepApiError && error.proposedResolution && (
<Subtitle className="mt-4">{error.proposedResolution}</Subtitle>
)}

<div className="error-image">
<Image src="/keep.svg" alt="Keep" width={150} height={150} />
Expand All @@ -48,36 +49,23 @@ export default function ErrorComponent({
onClick={() => signOut()}
color="orange"
variant="secondary"
className="mt-4 border border-orange-500 text-orange-500">
className="mt-4 border border-orange-500 text-orange-500"
>
<Text>Sign Out</Text>
</Button>
) : (
<Button
onClick={() => {
console.log("Refreshing...")
console.log("Refreshing...");
window.location.reload();
}}
color="orange"
variant="secondary"
className="mt-4 border border-orange-500 text-orange-500">
className="mt-4 border border-orange-500 text-orange-500"
>
<Text>Try Again!</Text>
</Button>
)}
</div>
)
}

// Custom Error Class
export class KeepApiError extends Error {
url: string;
proposedResolution: string;
statusCode: number | undefined;

constructor(message: string, url: string, proposedResolution: string, statusCode?: number) {
super(message);
this.name = "KeepApiError";
this.url = url;
this.proposedResolution = proposedResolution;
this.statusCode = statusCode;
}
);
}
15 changes: 12 additions & 3 deletions keep-ui/app/incidents/incident-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { Card, Title, Subtitle, Button, Badge } from "@tremor/react";
import Loading from "app/loading";
import React, { useState } from "react";
import { IncidentDto } from "./models";
import { IncidentDto, PaginatedIncidentsDto } from "@/app/incidents/models";
import CreateOrUpdateIncident from "./create-or-update-incident";
import IncidentsTable from "./incidents-table";
import { useIncidents, usePollIncidents } from "utils/hooks/useIncidents";
Expand All @@ -28,7 +28,11 @@ interface Filters {
affected_services: string[];
}

export default function IncidentList() {
export default function IncidentList({
initialData,
}: {
initialData?: PaginatedIncidentsDto;
}) {
const [incidentsPagination, setIncidentsPagination] = useState<Pagination>({
limit: 20,
offset: 0,
Expand Down Expand Up @@ -65,7 +69,12 @@ export default function IncidentList() {
incidentsPagination.limit,
incidentsPagination.offset,
incidentsSorting[0],
filters
filters,
{
revalidateOnFocus: false,
revalidateOnMount: !initialData,
fallbackData: initialData,
}
);
const {
data: predictedIncidents,
Expand Down
29 changes: 27 additions & 2 deletions keep-ui/app/incidents/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,32 @@
import { getServerSession } from "next-auth/next";
import IncidentList from "./incident-list";
import { getApiURL } from "@/utils/apiUrl";
import { authOptions } from "@/pages/api/auth/[...nextauth]";
import {
getIncidents,
GetIncidentsParams,
} from "@/entities/incidents/api/incidents";
import { PaginatedIncidentsDto } from "./models";

export default function Page() {
return <IncidentList />;
const defaultIncidentsParams: GetIncidentsParams = {
confirmed: true,
limit: 20,
offset: 0,
sorting: { id: "creation_time", desc: true },
filters: {},
};

export default async function Page() {
let incidents: PaginatedIncidentsDto | null = null;
try {
const session = await getServerSession(authOptions);
const apiUrl = getApiURL();

incidents = await getIncidents(apiUrl, session, defaultIncidentsParams);
} catch (error) {
console.log(error);
}
return <IncidentList initialData={incidents ?? undefined} />;
}

export const metadata = {
Expand Down
2 changes: 1 addition & 1 deletion keep-ui/app/providers/page.client.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";
import { defaultProvider, Provider } from "./providers";
import { useSession } from "next-auth/react";
import { KeepApiError } from "../error";
import { KeepApiError } from "@/shared/lib/KeepApiError";
import { useApiUrl } from "utils/hooks/useConfig";
import ProvidersTiles from "./providers-tiles";
import React, { useState, useEffect } from "react";
Expand Down
2 changes: 1 addition & 1 deletion keep-ui/app/workflows/builder/builder-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useEffect, useState } from "react";
import { useApiUrl } from "utils/hooks/useConfig";
import Loader from "./loader";
import { Provider } from "../../providers/providers";
import { KeepApiError } from "../../error";
import { KeepApiError } from "@/shared/lib/KeepApiError";
import { useProviders } from "utils/hooks/useProviders";

const Builder = dynamic(() => import("./builder"), {
Expand Down
52 changes: 52 additions & 0 deletions keep-ui/entities/incidents/api/incidents.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { PaginatedIncidentsDto } from "@/app/incidents/models";
import { fetcher } from "@/utils/fetcher";
import { Session } from "next-auth";

interface Filters {
status: string[];
severity: string[];
assignees: string[];
sources: string[];
affected_services: string[];
}

export type GetIncidentsParams = {
confirmed: boolean;
limit: number;
offset: number;
sorting: { id: string; desc: boolean };
filters: Filters | {};
};

export function buildIncidentsUrl(apiUrl: string, params: GetIncidentsParams) {
const filtersParams = new URLSearchParams();

Object.entries(params.filters).forEach(([key, value]) => {
if (value.length == 0) {
filtersParams.delete(key as string);
} else {
value.forEach((s: string) => {
filtersParams.append(key, s);
});
}
});

return `${apiUrl}/incidents?confirmed=${params.confirmed}&limit=${params.limit}&offset=${params.offset}&sorting=${
params.sorting.desc ? "-" : ""
}${params.sorting.id}&${filtersParams.toString()}`;
}

export async function getIncidents(
apiUrl: string,
session: Session | null,
params: GetIncidentsParams
) {
if (!session) {
return null;
}
const url = buildIncidentsUrl(apiUrl, params);
return (await fetcher(
url,
session.accessToken
)) as Promise<PaginatedIncidentsDto>;
}
24 changes: 24 additions & 0 deletions keep-ui/shared/lib/KeepApiError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Custom Error Class

export class KeepApiError extends Error {
url: string;
proposedResolution: string;
statusCode: number | undefined;

constructor(
message: string,
url: string,
proposedResolution: string,
statusCode?: number
) {
super(message);
this.name = "KeepApiError";
this.url = url;
this.proposedResolution = proposedResolution;
this.statusCode = statusCode;
}

toString() {
return `${this.name}: ${this.message} - ${this.url} - ${this.proposedResolution} - ${this.statusCode}`;
}
}
1 change: 1 addition & 0 deletions keep-ui/tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module.exports = {
"./components/**/*.{js,ts,jsx,tsx}",
"./entities/**/*.{js,ts,jsx,tsx}",
"./features/**/*.{js,ts,jsx,tsx}",
"./shared/**/*.{js,ts,jsx,tsx}",
"./node_modules/@tremor/**/*.{js,ts,jsx,tsx}",
],
darkMode: "class",
Expand Down
3 changes: 2 additions & 1 deletion keep-ui/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
"@/pages/*": ["./pages/*"],
"@/utils/*": ["./utils/*"],
"@/entities/*": ["./entities/*"],
"@/features/*": ["./features/*"]
"@/features/*": ["./features/*"],
"@/shared/*": ["./shared/*"]
}
},
"include": [
Expand Down
4 changes: 2 additions & 2 deletions keep-ui/utils/fetcher.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { KeepApiError } from '../app/error';
import { KeepApiError } from "@/shared/lib/KeepApiError";

export const fetcher = async (
url: string,
Expand All @@ -17,7 +17,7 @@ export const fetcher = async (
// if the response has detail field, throw the detail field
if (response.headers.get("content-type")?.includes("application/json")) {
const data = await response.json();
if(response.status === 401) {
if (response.status === 401) {
throw new KeepApiError(
`${data.message || data.detail}`,
url,
Expand Down
4 changes: 3 additions & 1 deletion keep-ui/utils/hooks/useIncidents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ export const useIncidents = (

return {
...swrValue,
isLoading: swrValue.isLoading || sessionStatus === "loading",
isLoading:
swrValue.isLoading ||
(!options.fallbackData && sessionStatus === "loading"),
};
};

Expand Down

0 comments on commit 9a9a310

Please sign in to comment.