From d1ae34f4d7e94922a592e72f9aef958fd3f11442 Mon Sep 17 00:00:00 2001 From: Nadeesha Cabral Date: Sat, 14 Dec 2024 09:08:53 +1100 Subject: [PATCH] feat: Add timestamp to service definitions and update UI --- app/components/cluster-details.tsx | 37 ++++++++++++++----- app/components/services-overview.tsx | 5 +++ control-plane/src/modules/contract.ts | 1 + control-plane/src/modules/management.ts | 10 +++-- .../src/modules/service-definitions.ts | 14 ++++--- 5 files changed, 48 insertions(+), 19 deletions(-) diff --git a/app/components/cluster-details.tsx b/app/components/cluster-details.tsx index 42ee16c3..cb319744 100644 --- a/app/components/cluster-details.tsx +++ b/app/components/cluster-details.tsx @@ -25,7 +25,7 @@ import { import { createErrorToast } from "@/lib/utils"; import { useAuth } from "@clerk/nextjs"; import { ClientInferResponseBody, ClientInferResponses } from "@ts-rest/core"; -import { formatRelative } from "date-fns"; +import { formatDistance, formatRelative } from "date-fns"; import { AppWindowIcon, Layers } from "lucide-react"; import ToolContextButton from "./chat/ToolContextButton"; import { DeadGrayCircle, DeadRedCircle, LiveGreenCircle } from "./circles"; @@ -41,6 +41,7 @@ export type Service = { description?: string; schema?: string; }[]; + timestamp: string; }; function toServiceName(name: string) { @@ -81,11 +82,12 @@ function ServiceCard({ - +
- Function - Description + Function + Description + Last Ping @@ -93,7 +95,7 @@ function ServiceCard({ ?.sort((a, b) => a.name.localeCompare(b.name)) .map((func) => ( - +
{toFunctionName(func.name, service.name)} @@ -105,9 +107,18 @@ function ServiceCard({ />
- + {func.description || "No description"} + + {new Date(service.timestamp) > new Date() ? ( +

+ Permanent Sync +

+ ) : ( + formatDistance(new Date(service.timestamp), new Date()) + )} +
))}
@@ -219,8 +230,6 @@ export function ClusterDetails({ (m) => Date.now() - new Date(m.lastPingAt!).getTime() < 1000 * 60 ).length; - const isHealthy = liveMachineCount > 0 && services.length > 0; - return (
@@ -230,7 +239,11 @@ export function ClusterDetails({ className="border bg-white hover:bg-gray-50 w-full h-10 px-3 justify-start relative" >
- {isHealthy ? : } + {liveMachineCount > 0 ? ( + + ) : ( + + )}
@@ -273,7 +286,11 @@ export function ClusterDetails({ className="border bg-white hover:bg-gray-50 w-full h-10 px-3 justify-start relative" >
- {isHealthy ? : } + {services.length > 0 ? ( + + ) : ( + + )}
diff --git a/app/components/services-overview.tsx b/app/components/services-overview.tsx index 3717d0f2..a8eaf628 100644 --- a/app/components/services-overview.tsx +++ b/app/components/services-overview.tsx @@ -23,6 +23,7 @@ export type Service = { description?: string; schema?: string; }[]; + timestamp: string; }; function toServiceName(name: string) { @@ -68,6 +69,7 @@ function ServiceCard({ Function Description + Last Ping @@ -90,6 +92,9 @@ function ServiceCard({ {func.description || "No description"} + + {service.timestamp || "No description"} + ))} diff --git a/control-plane/src/modules/contract.ts b/control-plane/src/modules/contract.ts index a54080f7..4e161fda 100644 --- a/control-plane/src/modules/contract.ts +++ b/control-plane/src/modules/contract.ts @@ -929,6 +929,7 @@ export const definition = { }), ) .optional(), + timestamp: z.date(), }), ), }, diff --git a/control-plane/src/modules/management.ts b/control-plane/src/modules/management.ts index 7ebc95df..94ca95aa 100644 --- a/control-plane/src/modules/management.ts +++ b/control-plane/src/modules/management.ts @@ -3,9 +3,7 @@ import { ulid } from "ulid"; import * as errors from "../utilities/errors"; import * as data from "./data"; import { randomName } from "./names"; -import { - storedServiceDefinitionSchema, -} from "./service-definitions"; +import { storedServiceDefinitionSchema } from "./service-definitions"; import { VersionedTexts } from "./versioned-text"; export const getClusters = async ({ @@ -197,12 +195,16 @@ export const getClusterServices = async ({ const services = await data.db .select({ definition: data.services.definition, + timestamp: data.services.timestamp, }) .from(data.services) .where(eq(data.services.cluster_id, clusterId)); const serviceDefinitions = storedServiceDefinitionSchema.parse( - services.map((s) => s.definition), + services.map((s) => ({ + ...(s.definition as object), + timestamp: s.timestamp, + })), ); return serviceDefinitions; diff --git a/control-plane/src/modules/service-definitions.ts b/control-plane/src/modules/service-definitions.ts index 36b680fc..7abd6652 100644 --- a/control-plane/src/modules/service-definitions.ts +++ b/control-plane/src/modules/service-definitions.ts @@ -43,6 +43,7 @@ export const storedServiceDefinitionSchema = z.array( z.object({ name: z.string(), description: z.string().optional(), + timestamp: z.date(), functions: z .array( z.object({ @@ -415,19 +416,22 @@ export const validateServiceRegistration = ({ } catch { throw new InvalidServiceRegistrationError( `${fn.name} cache.keyPath is invalid`, - "https://docs.inferable.ai/pages/functions#config-cache" - ) + "https://docs.inferable.ai/pages/functions#config-cache", + ); } } // Checks for customer auth handler const VERIFY_FUNCTION_NAME = "handleCustomAuth"; const VERIFY_FUNCTION_SERVICE = "default"; - if (service === VERIFY_FUNCTION_SERVICE && fn.name === VERIFY_FUNCTION_NAME) { + if ( + service === VERIFY_FUNCTION_SERVICE && + fn.name === VERIFY_FUNCTION_NAME + ) { if (!fn.schema) { throw new InvalidServiceRegistrationError( `${fn.name} must have a valid schema`, - "https://docs.inferable.ai/pages/auth#handlecustomerauth" + "https://docs.inferable.ai/pages/auth#handlecustomerauth", ); } @@ -437,7 +441,7 @@ export const validateServiceRegistration = ({ if (!schema.success) { throw new InvalidServiceRegistrationError( `${fn.name} schema is not valid`, - "https://docs.inferable.ai/pages/auth#handlecustomerauth" + "https://docs.inferable.ai/pages/auth#handlecustomerauth", ); } }