Skip to content

Commit

Permalink
feat: new topology applications UI
Browse files Browse the repository at this point in the history
  • Loading branch information
Kiryous committed Sep 26, 2024
1 parent eb93b1d commit a4af7fc
Show file tree
Hide file tree
Showing 30 changed files with 1,626 additions and 837 deletions.
2 changes: 1 addition & 1 deletion keep-ui/app/alerts/alert-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { IoMdClose } from "react-icons/io";
// import AlertMenu from "./alert-menu";
import AlertTimeline from "./alert-timeline";
import { useAlerts } from "utils/hooks/useAlerts";
import TopologyPage from "app/topology/topology";
import TopologyPage from "../topology/map/ui/topology-page";

type AlertSidebarProps = {
isOpen: boolean;
Expand Down
65 changes: 65 additions & 0 deletions keep-ui/app/topology/data/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { getApiURL } from "../../../utils/apiUrl";
import { fetcher } from "../../../utils/fetcher";
import { Session } from "next-auth";
import { TopologyApplication, TopologyService } from "../models";

const isNullOrUndefined = (value: unknown): value is null | undefined =>
value === null || value === undefined;

export function buildTopologyUrl({
providerId,
service,
environment,
}: {
providerId?: string;
service?: string;
environment?: string;
}) {
const apiUrl = getApiURL();

const baseUrl = `${apiUrl}/topology`;

if (
!isNullOrUndefined(providerId) &&
!isNullOrUndefined(service) &&
!isNullOrUndefined(environment)
) {
const params = new URLSearchParams({
provider_id: providerId,
service_id: service,
environment: environment,
});
return `${baseUrl}?${params.toString()}`;
}

return baseUrl;
}

export async function getApplications(session: Session | null) {
if (!session) {
return null;
}
const apiUrl = `${getApiURL()}/topology/applications`;
return (await fetcher(apiUrl, session.accessToken)) as Promise<
TopologyApplication[]
>;
}

export function getTopology(
session: Session | null,
{
providerId,
service,
environment,
}: {
providerId?: string;
service?: string;
environment?: string;
}
) {
if (!session) {
return null;
}
const url = buildTopologyUrl({ providerId, service, environment });
return fetcher(url, session.accessToken) as Promise<TopologyService[]>;
}
Empty file.
1 change: 1 addition & 0 deletions keep-ui/app/topology/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"use client";

import { useState } from "react";
import { ServiceSearchContext } from "./service-search-context";

Expand Down
27 changes: 19 additions & 8 deletions keep-ui/app/topology/models.tsx → keep-ui/app/topology/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,46 @@ export interface TopologyServiceDependency {
}

export interface TopologyService {
id: string;
id: number;
source_provider_id?: string;
repository?: string;
tags?: string[];
service: string;
display_name: string;
description?: string;
team?: string;
application?: string;
email?: string;
slack?: string;
dependencies: TopologyServiceDependency[];
ip_address?: string;
mac_address?: string;
manufacturer?: string;
applicationObject?: Application;
application_ids: string[];
// Added on client to optimize rendering
applications: TopologyApplicationMinimal[];
}

// We need to convert interface to type because only types are allowed in @xyflow/react
// https://github.com/xyflow/web/issues/486
export type ServiceNodeType = Node<InterfaceToType<TopologyService>, string>;

export type Application = {
export type TopologyNode = ServiceNodeType | Node;

export type TopologyServiceMinimal = {
id: number;
service: string;
name: string;
};

export type TopologyApplicationMinimal = {
id: string;
name: string;
};

export type TopologyApplication = {
id: string;
name: string;
description: string;
services: {
id: string;
name: string;
}[];
services: TopologyServiceMinimal[];
// TODO: Consider adding tags, cost of disruption, etc.
};
47 changes: 42 additions & 5 deletions keep-ui/app/topology/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,47 @@
import TopologyPage from "./topology";

export default function Page() {
return <TopologyPage />;
}
import { getServerSession } from "next-auth/next";
import { authOptions } from "../../pages/api/auth/[...nextauth]";
import { getApplications, getTopology } from "./data/api";
import { TopologyPageClient } from "./topology-client";
import { Subtitle, Title } from "@tremor/react";
import React from "react";

export const metadata = {
title: "Keep - Service Topology",
description: "See service topology and information about your services",
};

type PageProps = {
searchParams: {
provider?: string;
service?: string;
environment?: string;
};
};

export default async function Page({ searchParams }: PageProps) {
const session = await getServerSession(authOptions);

const applications = await getApplications(session);
const topologyServices = await getTopology(session, {
providerId: searchParams.provider,
service: searchParams.service,
environment: searchParams.environment,
});

return (
<>
<div className="flex w-full justify-between items-center mb-2">
<div>
<Title>Service Topology</Title>
<Subtitle>
Data describing the topology of components in your environment.
</Subtitle>
</div>
</div>
<TopologyPageClient
applications={applications}
topologyServices={topologyServices}
/>
</>
);
}
51 changes: 51 additions & 0 deletions keep-ui/app/topology/topology-client.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"use client";

import { Tab, TabGroup, TabList, TabPanel, TabPanels } from "@tremor/react";
import { TopologyMap } from "./ui/map";
import { ApplicationsList } from "./ui/applications/applications-list";
import { useContext, useEffect, useState } from "react";
import { ServiceSearchContext } from "./service-search-context";
import { TopologyApplication, TopologyService } from "./models";

export function TopologyPageClient({
applications,
topologyServices,
}: {
applications: TopologyApplication[];
topologyServices: TopologyService[];
}) {
const [tabIndex, setTabIndex] = useState(0);
const { selectedServiceId } = useContext(ServiceSearchContext);

useEffect(() => {
if (!selectedServiceId) {
return;
}
setTabIndex(0);
}, [selectedServiceId]);

return (
<TabGroup
id="topology-tabs"
className="h-[calc(100%-7rem)] flex flex-col"
index={tabIndex}
onIndexChange={setTabIndex}
>
<TabList className="mb-2">
<Tab>Topology Map</Tab>
<Tab>Applications</Tab>
</TabList>
<TabPanels className="flex-1 flex flex-col">
<TabPanel className="flex-1">
<TopologyMap
topologyApplications={applications}
topologyServices={topologyServices}
/>
</TabPanel>
<TabPanel className="flex-1">
<ApplicationsList applications={applications} />
</TabPanel>
</TabPanels>
</TabGroup>
);
}
89 changes: 0 additions & 89 deletions keep-ui/app/topology/topology.tsx

This file was deleted.

Loading

0 comments on commit a4af7fc

Please sign in to comment.