Skip to content

Commit

Permalink
feat: add topology applications (#1975)
Browse files Browse the repository at this point in the history
Co-authored-by: Tal Borenstein <[email protected]>
  • Loading branch information
Kiryous and talboren authored Sep 30, 2024
1 parent ede3eaf commit f06625e
Show file tree
Hide file tree
Showing 56 changed files with 3,178 additions and 610 deletions.
3 changes: 3 additions & 0 deletions docs/api-ref/topology/create-application.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
openapi: post /topology/applications
---
3 changes: 3 additions & 0 deletions docs/api-ref/topology/delete-application.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
openapi: delete /topology/applications/{application_id}
---
3 changes: 3 additions & 0 deletions docs/api-ref/topology/get-applications.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
openapi: get /topology/applications
---
3 changes: 3 additions & 0 deletions docs/api-ref/topology/update-application.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
openapi: put /topology/applications/{application_id}
---
8 changes: 7 additions & 1 deletion docs/mint.json
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,13 @@
},
{
"group": "topology",
"pages": ["api-ref/topology/get-topology-data"]
"pages": [
"api-ref/topology/get-topology-data",
"api-ref/topology/create-application",
"api-ref/topology/delete-application",
"api-ref/topology/get-applications",
"api-ref/topology/update-application"
]
},
{
"group": "alerts",
Expand Down
2 changes: 1 addition & 1 deletion docs/openapi.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions 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 { TopologyMap } from "../topology/ui/map";

type AlertSidebarProps = {
isOpen: boolean;
Expand Down Expand Up @@ -108,7 +108,7 @@ const AlertSidebar = ({ isOpen, toggle, alert }: AlertSidebarProps) => {
/>
</Card>
<Title>Related Services</Title>
<TopologyPage
<TopologyMap
providerId={alert.providerId || ""}
service={alert.service || ""}
environment={"unknown"}
Expand Down
19 changes: 10 additions & 9 deletions keep-ui/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const mulish = Mulish({

import { ToastContainer } from "react-toastify";
import Navbar from "components/navbar/Navbar";
import { TopologyPollingContextProvider } from "@/app/topology/model/TopologyPollingContext";

type RootLayoutProps = {
children: ReactNode;
Expand All @@ -23,15 +24,15 @@ export default async function RootLayout({ children }: RootLayoutProps) {
<html lang="en" className={`bg-gray-50 ${mulish.className}`}>
<body className="h-screen flex flex-col lg:grid lg:grid-cols-[fit-content(250px)_30px_auto] lg:grid-rows-1 lg:has-[aside[data-minimized='true']]:grid-cols-[0px_30px_auto]">
<NextAuthProvider>
{/* @ts-ignore-error Server Component */}
<Navbar />
{/* https://discord.com/channels/752553802359505017/1068089513253019688/1117731746922893333 */}
<main className="page-container flex flex-col col-start-3 overflow-auto">
<div className="flex-1">
{children}
</div>
<ToastContainer />
</main>
<TopologyPollingContextProvider>
{/* @ts-ignore-error Server Component */}
<Navbar />
{/* https://discord.com/channels/752553802359505017/1068089513253019688/1117731746922893333 */}
<main className="page-container flex flex-col col-start-3 overflow-auto">
<div className="flex-1">{children}</div>
<ToastContainer />
</main>
</TopologyPollingContextProvider>
</NextAuthProvider>

{/** footer */}
Expand Down
2 changes: 1 addition & 1 deletion keep-ui/app/mapping/create-or-edit-mapping.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import { getApiURL } from "utils/apiUrl";
import { useMappings } from "utils/hooks/useMappingRules";
import { MappingRule } from "./models";
import { CreateableSearchSelect } from "@/components/ui/CreateableSearchSelect";
import { useTopology } from "utils/hooks/useTopology";
import { useTopology } from "@/app/topology/model";

interface Props {
editRule: MappingRule | null;
Expand Down
65 changes: 65 additions & 0 deletions keep-ui/app/topology/api/index.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 "../model/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[]>;
}
111 changes: 0 additions & 111 deletions keep-ui/app/topology/custom-node.tsx

This file was deleted.

36 changes: 15 additions & 21 deletions keep-ui/app/topology/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,24 @@
"use client";
import { Subtitle, TextInput, Title } from "@tremor/react";

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

export default function Layout({ children }: { children: any }) {
const [serviceInput, setServiceInput] = useState<string>("");
const [serviceQuery, setServiceQuery] = useState<string>("");
const [selectedServiceId, setSelectedServiceId] = useState<string | null>(
null
);

return (
<main className="p-4 md:p-10 mx-auto max-w-full h-full">
<div className="flex w-full justify-between">
<div>
<Title>Service Topology</Title>
<Subtitle>
Data describing the topology of components in your environment.
</Subtitle>
</div>
<TextInput
placeholder="Search for a service"
value={serviceInput}
onValueChange={setServiceInput}
className="w-64 mt-2"
/>
</div>
<ServiceSearchContext.Provider value={serviceInput}>
{children}
</ServiceSearchContext.Provider>
</main>
<ServiceSearchContext.Provider
value={{
serviceQuery,
setServiceQuery,
selectedServiceId,
setSelectedServiceId,
}}
>
{children}
</ServiceSearchContext.Provider>
);
}
51 changes: 51 additions & 0 deletions keep-ui/app/topology/model/TopologyPollingContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"use client";

import React, { createContext, useContext, useEffect, useState } from "react";
import { useWebsocket } from "@/utils/hooks/usePusher";
import { toast } from "react-toastify";

interface TopologyUpdate {
providerType: string;
providerId: string;
}

const TopologyPollingContext = createContext<number>(0);

// Using this provider to avoid polling on every render
export const TopologyPollingContextProvider: React.FC<{
children: React.ReactNode;
}> = ({ children }) => {
const [pollTopology, setPollTopology] = useState(0);
const { bind, unbind } = useWebsocket();

useEffect(() => {
const handleIncoming = (data: TopologyUpdate) => {
toast.success(
`Topology pulled from ${data.providerId} (${data.providerType})`,
{ position: "top-right" }
);
setPollTopology((prev) => prev + 1);
};

bind("topology-update", handleIncoming);
return () => {
unbind("topology-update", handleIncoming);
};
}, [bind, unbind]);

return (
<TopologyPollingContext.Provider value={pollTopology}>
{children}
</TopologyPollingContext.Provider>
);
};

export function useTopologyPollingContext() {
const context = useContext(TopologyPollingContext);
if (context === undefined) {
throw new Error(
"useTopologyContext must be used within a TopologyContextProvider"
);
}
return context;
}
12 changes: 12 additions & 0 deletions keep-ui/app/topology/model/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export type {
TopologyService,
TopologyServiceMinimal,
TopologyApplication,
TopologyApplicationMinimal,
TopologyNode,
ServiceNodeType,
TopologyServiceDependency,
} from "./models";

export { useTopology } from "./useTopology";
export { useTopologyApplications } from "./useTopologyApplications";
Loading

0 comments on commit f06625e

Please sign in to comment.