From 305fa4a4766489341b89d027f08cd4e2f5cabca3 Mon Sep 17 00:00:00 2001 From: Nawaz Dhandala Date: Wed, 18 Mar 2026 14:21:39 +0000 Subject: [PATCH] Refactor and enhance various components and routes in the Dashboard and MobileApp - Improved formatting and readability in Kubernetes PodDetail and SideMenu components. - Added service count fetching and loading/error handling in Logs, Metrics, and Traces pages. - Updated Exception and Kubernetes routes for better readability. - Enhanced Postgres migration scripts for KubernetesCluster and KubernetesClusterLabel. - Minor formatting adjustments in MarkdownViewer and CriticalPath utility. - Refactored EmptyState, MonitorSummaryView, and hooks in MobileApp for improved clarity. - Fixed minor issues in MonitorDetailScreen and MonitorsScreen regarding status display. --- .../Kubernetes/DocumentationCard.tsx | 7 +- .../src/Components/Metrics/MetricsTable.tsx | 4 +- .../src/Components/Span/SpanViewer.tsx | 3 +- .../Components/Telemetry/Documentation.tsx | 105 +++++----- .../src/Components/Traces/FlameGraph.tsx | 19 +- .../src/Components/Traces/TraceExplorer.tsx | 45 ++--- .../src/Components/Traces/TraceServiceMap.tsx | 25 +-- .../src/Components/Traces/TraceTable.tsx | 13 +- .../src/Pages/Exceptions/Unresolved.tsx | 51 +++-- .../src/Pages/Kubernetes/View/Events.tsx | 134 ++++++------- .../src/Pages/Kubernetes/View/PodDetail.tsx | 7 +- .../src/Pages/Kubernetes/View/SideMenu.tsx | 8 +- .../Dashboard/src/Pages/Logs/Index.tsx | 47 ++++- .../Dashboard/src/Pages/Metrics/Index.tsx | 52 +++-- .../Dashboard/src/Pages/Traces/Index.tsx | 47 ++++- .../Dashboard/src/Routes/ExceptionsRoutes.tsx | 4 +- .../Dashboard/src/Routes/KubernetesRoutes.tsx | 64 +++++-- .../1773761409952-MigrationName.ts | 179 +++++++++++++----- .../Postgres/SchemaMigrations/Index.ts | 2 +- Common/Server/Utils/StartServer.ts | 5 +- .../Markdown.tsx/MarkdownViewer.tsx | 26 ++- Common/Utils/Traces/CriticalPath.ts | 51 ++--- MobileApp/src/components/EmptyState.tsx | 8 +- .../src/components/MonitorSummaryView.tsx | 5 +- MobileApp/src/hooks/useAllProjectCounts.ts | 13 +- MobileApp/src/hooks/useAllProjectMonitors.ts | 13 +- MobileApp/src/screens/MonitorDetailScreen.tsx | 2 +- MobileApp/src/screens/MonitorsScreen.tsx | 3 +- 28 files changed, 581 insertions(+), 361 deletions(-) diff --git a/App/FeatureSet/Dashboard/src/Components/Kubernetes/DocumentationCard.tsx b/App/FeatureSet/Dashboard/src/Components/Kubernetes/DocumentationCard.tsx index 16e0b5f0c9..c781e22020 100644 --- a/App/FeatureSet/Dashboard/src/Components/Kubernetes/DocumentationCard.tsx +++ b/App/FeatureSet/Dashboard/src/Components/Kubernetes/DocumentationCard.tsx @@ -149,10 +149,9 @@ const KubernetesDocumentationCard: FunctionComponent = ( )} value={ ingestionKeys - .filter( - (key: TelemetryIngestionKey) => - key.id?.toString() === selectedKeyId, - ) + .filter((key: TelemetryIngestionKey) => { + return key.id?.toString() === selectedKeyId; + }) .map((key: TelemetryIngestionKey): DropdownOption => { return { value: key.id?.toString() || "", diff --git a/App/FeatureSet/Dashboard/src/Components/Metrics/MetricsTable.tsx b/App/FeatureSet/Dashboard/src/Components/Metrics/MetricsTable.tsx index 498f33a36e..e68cdff394 100644 --- a/App/FeatureSet/Dashboard/src/Components/Metrics/MetricsTable.tsx +++ b/App/FeatureSet/Dashboard/src/Components/Metrics/MetricsTable.tsx @@ -17,7 +17,9 @@ import MetricsAggregationType from "Common/Types/Metrics/MetricsAggregationType" export interface ComponentProps { serviceIds?: Array | undefined; - onFetchSuccess?: ((data: Array, totalCount: number) => void) | undefined; + onFetchSuccess?: + | ((data: Array, totalCount: number) => void) + | undefined; } const MetricsTable: FunctionComponent = ( diff --git a/App/FeatureSet/Dashboard/src/Components/Span/SpanViewer.tsx b/App/FeatureSet/Dashboard/src/Components/Span/SpanViewer.tsx index 31e7c5e5e5..8c7308a0ed 100644 --- a/App/FeatureSet/Dashboard/src/Components/Span/SpanViewer.tsx +++ b/App/FeatureSet/Dashboard/src/Components/Span/SpanViewer.tsx @@ -622,8 +622,7 @@ const SpanViewer: FunctionComponent = ( - {link.attributes && - Object.keys(link.attributes).length > 0 ? ( + {link.attributes && Object.keys(link.attributes).length > 0 ? (
Attributes diff --git a/App/FeatureSet/Dashboard/src/Components/Telemetry/Documentation.tsx b/App/FeatureSet/Dashboard/src/Components/Telemetry/Documentation.tsx index 873d806e02..60ce6d2fed 100644 --- a/App/FeatureSet/Dashboard/src/Components/Telemetry/Documentation.tsx +++ b/App/FeatureSet/Dashboard/src/Components/Telemetry/Documentation.tsx @@ -816,42 +816,39 @@ const TelemetryDocumentation: FunctionComponent = ( loadIngestionKeys().catch(() => {}); }, []); - const loadIngestionKeys: () => Promise = - async (): Promise => { - try { - setIsLoadingKeys(true); - setKeyError(""); - const result: ListResult = - await ModelAPI.getList({ - modelType: TelemetryIngestionKey, - query: { - projectId: ProjectUtil.getCurrentProjectId()!, - }, - limit: 50, - skip: 0, - select: { - _id: true, - name: true, - secretKey: true, - description: true, - }, - sort: {}, - }); + const loadIngestionKeys: () => Promise = async (): Promise => { + try { + setIsLoadingKeys(true); + setKeyError(""); + const result: ListResult = + await ModelAPI.getList({ + modelType: TelemetryIngestionKey, + query: { + projectId: ProjectUtil.getCurrentProjectId()!, + }, + limit: 50, + skip: 0, + select: { + _id: true, + name: true, + secretKey: true, + description: true, + }, + sort: {}, + }); - setIngestionKeys(result.data); + setIngestionKeys(result.data); - // Auto-select the first key if available and none selected - if (result.data.length > 0 && !selectedKeyId) { - setSelectedKeyId( - result.data[0]!.id?.toString() || "", - ); - } - } catch (err) { - setKeyError(API.getFriendlyErrorMessage(err as Error)); - } finally { - setIsLoadingKeys(false); + // Auto-select the first key if available and none selected + if (result.data.length > 0 && !selectedKeyId) { + setSelectedKeyId(result.data[0]!.id?.toString() || ""); } - }; + } catch (err) { + setKeyError(API.getFriendlyErrorMessage(err as Error)); + } finally { + setIsLoadingKeys(false); + } + }; // Get the selected key object const selectedKey: TelemetryIngestionKey | undefined = useMemo(() => { @@ -944,9 +941,7 @@ const TelemetryDocumentation: FunctionComponent = (
{stepNumber}
- {!isLast && ( -
- )} + {!isLast &&
}
{/* Step content */}
@@ -1023,15 +1018,19 @@ const TelemetryDocumentation: FunctionComponent = (
{ - return { - value: key.id?.toString() || "", - label: key.name || "Unnamed Key", - }; - })} + options={ingestionKeys.map( + (key: TelemetryIngestionKey): DropdownOption => { + return { + value: key.id?.toString() || "", + label: key.name || "Unnamed Key", + }; + }, + )} value={ ingestionKeys - .filter((key: TelemetryIngestionKey) => key.id?.toString() === selectedKeyId) + .filter((key: TelemetryIngestionKey) => { + return key.id?.toString() === selectedKeyId; + }) .map((key: TelemetryIngestionKey): DropdownOption => { return { value: key.id?.toString() || "", @@ -1039,7 +1038,9 @@ const TelemetryDocumentation: FunctionComponent = ( }; })[0] } - onChange={(value: DropdownValue | Array | null) => { + onChange={( + value: DropdownValue | Array | null, + ) => { if (value) { setSelectedKeyId(value.toString()); } @@ -1195,7 +1196,11 @@ const TelemetryDocumentation: FunctionComponent = ( {renderStep( 2, "Install Dependencies", - `Install the OpenTelemetry SDK and exporters for ${languages.find((l: LanguageOption) => { return l.key === selectedLanguage; })?.label || selectedLanguage}.`, + `Install the OpenTelemetry SDK and exporters for ${ + languages.find((l: LanguageOption) => { + return l.key === selectedLanguage; + })?.label || selectedLanguage + }.`, = ( 4, "Run FluentBit", "Start FluentBit with your configuration file.", - , + , true, )}
@@ -1340,10 +1342,7 @@ const TelemetryDocumentation: FunctionComponent = ( 4, "Run Fluentd", "Start Fluentd with your configuration.", - , + , true, )}
diff --git a/App/FeatureSet/Dashboard/src/Components/Traces/FlameGraph.tsx b/App/FeatureSet/Dashboard/src/Components/Traces/FlameGraph.tsx index c7ce469d1b..9986ce0c34 100644 --- a/App/FeatureSet/Dashboard/src/Components/Traces/FlameGraph.tsx +++ b/App/FeatureSet/Dashboard/src/Components/Traces/FlameGraph.tsx @@ -37,7 +37,8 @@ const FlameGraph: FunctionComponent = ( const [hoveredSpanId, setHoveredSpanId] = React.useState(null); const [focusedSpanId, setFocusedSpanId] = React.useState(null); - const containerRef: React.RefObject = React.useRef(null); + const containerRef: React.RefObject = + React.useRef(null); // Build span data for critical path utility const spanDataList: SpanData[] = React.useMemo(() => { @@ -90,9 +91,7 @@ const FlameGraph: FunctionComponent = ( } } - const getServiceInfo = ( - span: Span, - ): { color: Color; name: string } => { + const getServiceInfo = (span: Span): { color: Color; name: string } => { const service: Service | undefined = telemetryServices.find( (s: Service) => { return s._id?.toString() === span.serviceId?.toString(); @@ -123,7 +122,9 @@ const FlameGraph: FunctionComponent = ( startTimeUnixNano: span.startTimeUnixNano!, endTimeUnixNano: span.endTimeUnixNano!, durationUnixNano: span.durationUnixNano!, - selfTimeUnixNano: selfTime ? selfTime.selfTimeUnixNano : span.durationUnixNano!, + selfTimeUnixNano: selfTime + ? selfTime.selfTimeUnixNano + : span.durationUnixNano!, serviceColor: serviceInfo.color, serviceName: serviceInfo.name, }; @@ -220,13 +221,9 @@ const FlameGraph: FunctionComponent = ( } const leftPercent: number = - totalDuration > 0 - ? ((nodeStart - viewStart) / totalDuration) * 100 - : 0; + totalDuration > 0 ? ((nodeStart - viewStart) / totalDuration) * 100 : 0; const widthPercent: number = - totalDuration > 0 - ? ((nodeEnd - nodeStart) / totalDuration) * 100 - : 0; + totalDuration > 0 ? ((nodeEnd - nodeStart) / totalDuration) * 100 : 0; const isHovered: boolean = hoveredSpanId === node.span.spanId; const isSelected: boolean = selectedSpanId === node.span.spanId; diff --git a/App/FeatureSet/Dashboard/src/Components/Traces/TraceExplorer.tsx b/App/FeatureSet/Dashboard/src/Components/Traces/TraceExplorer.tsx index ba96baa5ef..6fa69af3b3 100644 --- a/App/FeatureSet/Dashboard/src/Components/Traces/TraceExplorer.tsx +++ b/App/FeatureSet/Dashboard/src/Components/Traces/TraceExplorer.tsx @@ -712,7 +712,13 @@ const TraceExplorer: FunctionComponent = ( }); } return filtered; - }, [spans, showErrorsOnly, selectedServiceIds, spanSearchText, telemetryServices]); + }, [ + spans, + showErrorsOnly, + selectedServiceIds, + spanSearchText, + telemetryServices, + ]); // Search match count for display const searchMatchCount: number = React.useMemo(() => { @@ -1288,11 +1294,9 @@ const TraceExplorer: FunctionComponent = (
{serviceBreakdown.map((breakdown: ServiceBreakdown) => { - const service: Service | undefined = - telemetryServices.find((s: Service) => { - return ( - s._id?.toString() === breakdown.serviceId - ); - }); - const serviceName: string = - service?.name || "Unknown"; + const service: Service | undefined = telemetryServices.find( + (s: Service) => { + return s._id?.toString() === breakdown.serviceId; + }, + ); + const serviceName: string = service?.name || "Unknown"; const serviceColor: string = String( - (service?.serviceColor as unknown as string) || - "#6366f1", + (service?.serviceColor as unknown as string) || "#6366f1", ); const percent: number = Math.min( breakdown.percentOfTrace, @@ -1446,7 +1447,10 @@ const TraceExplorer: FunctionComponent = ( ); return ( -
+
= (
{SpanUtil.getSpanDurationAsString({ - spanDurationInUnixNano: - breakdown.selfTimeUnixNano, + spanDurationInUnixNano: breakdown.selfTimeUnixNano, divisibilityFactor: divisibilityFactor, })}{" "} ({percent.toFixed(1)}%) @@ -1526,9 +1529,7 @@ const TraceExplorer: FunctionComponent = ( setSelectedSpans([spanId]); }} selectedSpanId={ - selectedSpans.length > 0 - ? selectedSpans[0] - : undefined + selectedSpans.length > 0 ? selectedSpans[0] : undefined } />
diff --git a/App/FeatureSet/Dashboard/src/Components/Traces/TraceServiceMap.tsx b/App/FeatureSet/Dashboard/src/Components/Traces/TraceServiceMap.tsx index 427be6ef4f..811f263837 100644 --- a/App/FeatureSet/Dashboard/src/Components/Traces/TraceServiceMap.tsx +++ b/App/FeatureSet/Dashboard/src/Components/Traces/TraceServiceMap.tsx @@ -135,8 +135,10 @@ const TraceServiceMap: FunctionComponent = ( ); } - // Layout: arrange nodes in a topological order based on edges - // Simple layout: find entry nodes and lay out left-to-right + /* + * Layout: arrange nodes in a topological order based on edges + * Simple layout: find entry nodes and lay out left-to-right + */ const { nodePositions, layoutWidth, layoutHeight } = React.useMemo(() => { // Build adjacency list const adjList: Map = new Map(); @@ -151,10 +153,7 @@ const TraceServiceMap: FunctionComponent = ( const neighbors: string[] = adjList.get(edge.fromServiceId) || []; neighbors.push(edge.toServiceId); adjList.set(edge.fromServiceId, neighbors); - inDegree.set( - edge.toServiceId, - (inDegree.get(edge.toServiceId) || 0) + 1, - ); + inDegree.set(edge.toServiceId, (inDegree.get(edge.toServiceId) || 0) + 1); } // Topological sort using BFS (Kahn's algorithm) @@ -333,18 +332,16 @@ const TraceServiceMap: FunctionComponent = ( refY="3" orient="auto" > - + {/* Render nodes */} {nodes.map((node: ServiceNode) => { - const pos: { x: number; y: number } | undefined = - nodePositions.get(node.serviceId); + const pos: { x: number; y: number } | undefined = nodePositions.get( + node.serviceId, + ); if (!pos) { return null; } @@ -355,9 +352,7 @@ const TraceServiceMap: FunctionComponent = (
| undefined; isMinimalTable?: boolean | undefined; noItemsMessage?: string | undefined; - onFetchSuccess?: ((data: Array, totalCount: number) => void) | undefined; + onFetchSuccess?: + | ((data: Array, totalCount: number) => void) + | undefined; } const TraceTable: FunctionComponent = ( @@ -312,12 +314,9 @@ const TraceTable: FunctionComponent = ( ); } return Promise.resolve( - RouteUtil.populateRouteParams( - RouteMap[PageMap.TRACE_VIEW]!, - { - modelId: span.traceId!.toString(), - }, - ), + RouteUtil.populateRouteParams(RouteMap[PageMap.TRACE_VIEW]!, { + modelId: span.traceId!.toString(), + }), ); }} filters={[ diff --git a/App/FeatureSet/Dashboard/src/Pages/Exceptions/Unresolved.tsx b/App/FeatureSet/Dashboard/src/Pages/Exceptions/Unresolved.tsx index 83e04f3209..227c384a54 100644 --- a/App/FeatureSet/Dashboard/src/Pages/Exceptions/Unresolved.tsx +++ b/App/FeatureSet/Dashboard/src/Pages/Exceptions/Unresolved.tsx @@ -4,11 +4,15 @@ import TelemetryDocumentation from "../../Components/Telemetry/Documentation"; import React, { FunctionComponent, ReactElement, - useCallback, + useEffect, useState, } from "react"; import ExceptionsTable from "../../Components/Exceptions/ExceptionsTable"; -import TelemetryException from "Common/Models/DatabaseModels/TelemetryException"; +import Service from "Common/Models/DatabaseModels/Service"; +import ModelAPI from "Common/UI/Utils/ModelAPI/ModelAPI"; +import API from "Common/UI/Utils/API/API"; +import PageLoader from "Common/UI/Components/Loader/PageLoader"; +import { PromiseVoidFunction } from "Common/Types/FunctionTypes"; const UnresolvedExceptionsPage: FunctionComponent = ( props: PageComponentProps, @@ -16,17 +20,29 @@ const UnresolvedExceptionsPage: FunctionComponent = ( const disableTelemetryForThisProject: boolean = props.currentProject?.reseller?.enableTelemetryFeatures === false; - const [hasData, setHasData] = useState(undefined); + const [serviceCount, setServiceCount] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(""); - const handleFetchSuccess: ( - data: Array, - totalCount: number, - ) => void = useCallback( - (_data: Array, totalCount: number) => { - setHasData(totalCount > 0); - }, - [], - ); + const fetchServiceCount: PromiseVoidFunction = async (): Promise => { + setIsLoading(true); + try { + const count: number = await ModelAPI.count({ + modelType: Service, + query: {}, + }); + setServiceCount(count); + } catch (err) { + setError(API.getFriendlyMessage(err)); + } + setIsLoading(false); + }; + + useEffect(() => { + fetchServiceCount().catch((err: Error) => { + setError(API.getFriendlyMessage(err)); + }); + }, []); if (disableTelemetryForThisProject) { return ( @@ -34,7 +50,15 @@ const UnresolvedExceptionsPage: FunctionComponent = ( ); } - if (hasData === false) { + if (isLoading) { + return ; + } + + if (error) { + return ; + } + + if (serviceCount === 0) { return ; } @@ -46,7 +70,6 @@ const UnresolvedExceptionsPage: FunctionComponent = ( }} title="Unresolved Exceptions" description="All the exceptions that have not been resolved." - onFetchSuccess={handleFetchSuccess} /> ); }; diff --git a/App/FeatureSet/Dashboard/src/Pages/Kubernetes/View/Events.tsx b/App/FeatureSet/Dashboard/src/Pages/Kubernetes/View/Events.tsx index 42d28436ad..7b443982e3 100644 --- a/App/FeatureSet/Dashboard/src/Pages/Kubernetes/View/Events.tsx +++ b/App/FeatureSet/Dashboard/src/Pages/Kubernetes/View/Events.tsx @@ -65,26 +65,25 @@ const KubernetesClusterEvents: FunctionComponent< const endDate: Date = OneUptimeDate.getCurrentDate(); const startDate: Date = OneUptimeDate.addRemoveHours(endDate, -24); - const listResult: ListResult = - await AnalyticsModelAPI.getList({ - modelType: Log, - query: { - projectId: ProjectUtil.getCurrentProjectId()!.toString(), - time: new InBetween(startDate, endDate), - }, - limit: 200, - skip: 0, - select: { - time: true, - body: true, - severityText: true, - attributes: true, - }, - sort: { - time: SortOrder.Descending, - }, - requestOptions: {}, - }); + const listResult: ListResult = await AnalyticsModelAPI.getList({ + modelType: Log, + query: { + projectId: ProjectUtil.getCurrentProjectId()!.toString(), + time: new InBetween(startDate, endDate), + }, + limit: 200, + skip: 0, + select: { + time: true, + body: true, + severityText: true, + attributes: true, + }, + sort: { + time: SortOrder.Descending, + }, + requestOptions: {}, + }); // Helper to extract a string value from OTLP kvlistValue const getKvValue = ( @@ -94,7 +93,9 @@ const KubernetesClusterEvents: FunctionComponent< if (!kvList) { return ""; } - const values = (kvList as JSONObject)["values"] as Array | undefined; + const values = (kvList as JSONObject)["values"] as + | Array + | undefined; if (!values) { return ""; } @@ -128,7 +129,9 @@ const KubernetesClusterEvents: FunctionComponent< if (!kvList) { return ""; } - const values = (kvList as JSONObject)["values"] as Array | undefined; + const values = (kvList as JSONObject)["values"] as + | Array + | undefined; if (!values) { return ""; } @@ -193,10 +196,14 @@ const KubernetesClusterEvents: FunctionComponent< const note: string = getKvValue(objectKvList, "note") || ""; // Get object details from "regarding" sub-object - const objectKind: string = getNestedKvValue(objectKvList, "regarding", "kind") || ""; - const objectName: string = getNestedKvValue(objectKvList, "regarding", "name") || ""; - const namespace: string = getNestedKvValue(objectKvList, "regarding", "namespace") || - getNestedKvValue(objectKvList, "metadata", "namespace") || ""; + const objectKind: string = + getNestedKvValue(objectKvList, "regarding", "kind") || ""; + const objectName: string = + getNestedKvValue(objectKvList, "regarding", "name") || ""; + const namespace: string = + getNestedKvValue(objectKvList, "regarding", "namespace") || + getNestedKvValue(objectKvList, "metadata", "namespace") || + ""; if (eventType || reason) { k8sEvents.push({ @@ -275,47 +282,40 @@ const KubernetesClusterEvents: FunctionComponent< - {events.map( - (event: KubernetesEvent, index: number) => { - const isWarning: boolean = - event.type.toLowerCase() === "warning"; - return ( - - - {event.timestamp} - - - - {event.type} - - - - {event.reason} - - - {event.objectKind}/{event.objectName} - - - {event.namespace} - - - {event.message} - - - ); - }, - )} + {events.map((event: KubernetesEvent, index: number) => { + const isWarning: boolean = + event.type.toLowerCase() === "warning"; + return ( + + + {event.timestamp} + + + + {event.type} + + + + {event.reason} + + + {event.objectKind}/{event.objectName} + + + {event.namespace} + + + {event.message} + + + ); + })}
diff --git a/App/FeatureSet/Dashboard/src/Pages/Kubernetes/View/PodDetail.tsx b/App/FeatureSet/Dashboard/src/Pages/Kubernetes/View/PodDetail.tsx index b52f2b9a09..a78c03ccdb 100644 --- a/App/FeatureSet/Dashboard/src/Pages/Kubernetes/View/PodDetail.tsx +++ b/App/FeatureSet/Dashboard/src/Pages/Kubernetes/View/PodDetail.tsx @@ -205,7 +205,12 @@ const KubernetesClusterPodDetail: FunctionComponent< onChange={(data: MetricViewData) => { setMetricViewData({ ...data, - queryConfigs: [podCpuQuery, podMemoryQuery, cpuQuery, memoryQuery], + queryConfigs: [ + podCpuQuery, + podMemoryQuery, + cpuQuery, + memoryQuery, + ], formulaConfigs: [], }); }} diff --git a/App/FeatureSet/Dashboard/src/Pages/Kubernetes/View/SideMenu.tsx b/App/FeatureSet/Dashboard/src/Pages/Kubernetes/View/SideMenu.tsx index 0ddce2b3a7..ff62834736 100644 --- a/App/FeatureSet/Dashboard/src/Pages/Kubernetes/View/SideMenu.tsx +++ b/App/FeatureSet/Dashboard/src/Pages/Kubernetes/View/SideMenu.tsx @@ -32,9 +32,7 @@ const KubernetesClusterSideMenu: FunctionComponent = ( link={{ title: "Documentation", to: RouteUtil.populateRouteParams( - RouteMap[ - PageMap.KUBERNETES_CLUSTER_VIEW_DOCUMENTATION - ] as Route, + RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_DOCUMENTATION] as Route, { modelId: props.modelId }, ), }} @@ -80,9 +78,7 @@ const KubernetesClusterSideMenu: FunctionComponent = ( link={{ title: "Control Plane", to: RouteUtil.populateRouteParams( - RouteMap[ - PageMap.KUBERNETES_CLUSTER_VIEW_CONTROL_PLANE - ] as Route, + RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_CONTROL_PLANE] as Route, { modelId: props.modelId }, ), }} diff --git a/App/FeatureSet/Dashboard/src/Pages/Logs/Index.tsx b/App/FeatureSet/Dashboard/src/Pages/Logs/Index.tsx index 9e42bc6ee8..57a19db08d 100644 --- a/App/FeatureSet/Dashboard/src/Pages/Logs/Index.tsx +++ b/App/FeatureSet/Dashboard/src/Pages/Logs/Index.tsx @@ -3,11 +3,16 @@ import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage"; import React, { FunctionComponent, ReactElement, - useCallback, + useEffect, useState, } from "react"; import DashboardLogsViewer from "../../Components/Logs/LogsViewer"; import TelemetryDocumentation from "../../Components/Telemetry/Documentation"; +import Service from "Common/Models/DatabaseModels/Service"; +import ModelAPI from "Common/UI/Utils/ModelAPI/ModelAPI"; +import API from "Common/UI/Utils/API/API"; +import PageLoader from "Common/UI/Components/Loader/PageLoader"; +import { PromiseVoidFunction } from "Common/Types/FunctionTypes"; const LogsPage: FunctionComponent = ( props: PageComponentProps, @@ -15,15 +20,30 @@ const LogsPage: FunctionComponent = ( const disableTelemetryForThisProject: boolean = props.currentProject?.reseller?.enableTelemetryFeatures === false; - const [hasData, setHasData] = useState(undefined); + const [serviceCount, setServiceCount] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(""); const [showDocs, setShowDocs] = useState(false); - const handleCountChange: (count: number) => void = useCallback( - (count: number) => { - setHasData(count > 0); - }, - [], - ); + const fetchServiceCount: PromiseVoidFunction = async (): Promise => { + setIsLoading(true); + try { + const count: number = await ModelAPI.count({ + modelType: Service, + query: {}, + }); + setServiceCount(count); + } catch (err) { + setError(API.getFriendlyMessage(err)); + } + setIsLoading(false); + }; + + useEffect(() => { + fetchServiceCount().catch((err: Error) => { + setError(API.getFriendlyMessage(err)); + }); + }, []); if (disableTelemetryForThisProject) { return ( @@ -31,6 +51,14 @@ const LogsPage: FunctionComponent = ( ); } + if (isLoading) { + return ; + } + + if (error) { + return ; + } + if (showDocs) { return ( = ( ); } - if (hasData === false) { + if (serviceCount === 0) { return ; } @@ -53,7 +81,6 @@ const LogsPage: FunctionComponent = ( limit={100} enableRealtime={true} id="logs" - onCountChange={handleCountChange} onShowDocumentation={() => { setShowDocs(true); }} diff --git a/App/FeatureSet/Dashboard/src/Pages/Metrics/Index.tsx b/App/FeatureSet/Dashboard/src/Pages/Metrics/Index.tsx index 2f69699954..acfa75e49b 100644 --- a/App/FeatureSet/Dashboard/src/Pages/Metrics/Index.tsx +++ b/App/FeatureSet/Dashboard/src/Pages/Metrics/Index.tsx @@ -4,11 +4,15 @@ import TelemetryDocumentation from "../../Components/Telemetry/Documentation"; import React, { FunctionComponent, ReactElement, - useCallback, + useEffect, useState, } from "react"; import MetricsTable from "../../Components/Metrics/MetricsTable"; -import MetricType from "Common/Models/DatabaseModels/MetricType"; +import Service from "Common/Models/DatabaseModels/Service"; +import ModelAPI from "Common/UI/Utils/ModelAPI/ModelAPI"; +import API from "Common/UI/Utils/API/API"; +import PageLoader from "Common/UI/Components/Loader/PageLoader"; +import { PromiseVoidFunction } from "Common/Types/FunctionTypes"; const MetricsPage: FunctionComponent = ( props: PageComponentProps, @@ -16,17 +20,29 @@ const MetricsPage: FunctionComponent = ( const disableTelemetryForThisProject: boolean = props.currentProject?.reseller?.enableTelemetryFeatures === false; - const [hasData, setHasData] = useState(undefined); + const [serviceCount, setServiceCount] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(""); - const handleFetchSuccess: ( - data: Array, - totalCount: number, - ) => void = useCallback( - (_data: Array, totalCount: number) => { - setHasData(totalCount > 0); - }, - [], - ); + const fetchServiceCount: PromiseVoidFunction = async (): Promise => { + setIsLoading(true); + try { + const count: number = await ModelAPI.count({ + modelType: Service, + query: {}, + }); + setServiceCount(count); + } catch (err) { + setError(API.getFriendlyMessage(err)); + } + setIsLoading(false); + }; + + useEffect(() => { + fetchServiceCount().catch((err: Error) => { + setError(API.getFriendlyMessage(err)); + }); + }, []); if (disableTelemetryForThisProject) { return ( @@ -34,11 +50,19 @@ const MetricsPage: FunctionComponent = ( ); } - if (hasData === false) { + if (isLoading) { + return ; + } + + if (error) { + return ; + } + + if (serviceCount === 0) { return ; } - return ; + return ; }; export default MetricsPage; diff --git a/App/FeatureSet/Dashboard/src/Pages/Traces/Index.tsx b/App/FeatureSet/Dashboard/src/Pages/Traces/Index.tsx index 856f52bdba..d482e618dc 100644 --- a/App/FeatureSet/Dashboard/src/Pages/Traces/Index.tsx +++ b/App/FeatureSet/Dashboard/src/Pages/Traces/Index.tsx @@ -4,11 +4,15 @@ import TelemetryDocumentation from "../../Components/Telemetry/Documentation"; import React, { FunctionComponent, ReactElement, - useCallback, + useEffect, useState, } from "react"; import TraceTable from "../../Components/Traces/TraceTable"; -import Span from "Common/Models/AnalyticsModels/Span"; +import Service from "Common/Models/DatabaseModels/Service"; +import ModelAPI from "Common/UI/Utils/ModelAPI/ModelAPI"; +import API from "Common/UI/Utils/API/API"; +import PageLoader from "Common/UI/Components/Loader/PageLoader"; +import { PromiseVoidFunction } from "Common/Types/FunctionTypes"; const TracesPage: FunctionComponent = ( props: PageComponentProps, @@ -16,12 +20,29 @@ const TracesPage: FunctionComponent = ( const disableTelemetryForThisProject: boolean = props.currentProject?.reseller?.enableTelemetryFeatures === false; - const [hasData, setHasData] = useState(undefined); + const [serviceCount, setServiceCount] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(""); - const handleFetchSuccess: (data: Array, totalCount: number) => void = - useCallback((_data: Array, totalCount: number) => { - setHasData(totalCount > 0); - }, []); + const fetchServiceCount: PromiseVoidFunction = async (): Promise => { + setIsLoading(true); + try { + const count: number = await ModelAPI.count({ + modelType: Service, + query: {}, + }); + setServiceCount(count); + } catch (err) { + setError(API.getFriendlyMessage(err)); + } + setIsLoading(false); + }; + + useEffect(() => { + fetchServiceCount().catch((err: Error) => { + setError(API.getFriendlyMessage(err)); + }); + }, []); if (disableTelemetryForThisProject) { return ( @@ -29,11 +50,19 @@ const TracesPage: FunctionComponent = ( ); } - if (hasData === false) { + if (isLoading) { + return ; + } + + if (error) { + return ; + } + + if (serviceCount === 0) { return ; } - return ; + return ; }; export default TracesPage; diff --git a/App/FeatureSet/Dashboard/src/Routes/ExceptionsRoutes.tsx b/App/FeatureSet/Dashboard/src/Routes/ExceptionsRoutes.tsx index 73238b78af..2d9f367dcd 100644 --- a/App/FeatureSet/Dashboard/src/Routes/ExceptionsRoutes.tsx +++ b/App/FeatureSet/Dashboard/src/Routes/ExceptionsRoutes.tsx @@ -64,9 +64,7 @@ const ExceptionsRoutes: FunctionComponent = ( element={ } /> diff --git a/App/FeatureSet/Dashboard/src/Routes/KubernetesRoutes.tsx b/App/FeatureSet/Dashboard/src/Routes/KubernetesRoutes.tsx index f842cd3b8b..ae8920621d 100644 --- a/App/FeatureSet/Dashboard/src/Routes/KubernetesRoutes.tsx +++ b/App/FeatureSet/Dashboard/src/Routes/KubernetesRoutes.tsx @@ -61,81 +61,113 @@ const KubernetesRoutes: FunctionComponent = ( /> } /> } /> } /> } /> } /> } /> } /> } /> diff --git a/Common/Server/Infrastructure/Postgres/SchemaMigrations/1773761409952-MigrationName.ts b/Common/Server/Infrastructure/Postgres/SchemaMigrations/1773761409952-MigrationName.ts index c74159f8c9..31157ca536 100644 --- a/Common/Server/Infrastructure/Postgres/SchemaMigrations/1773761409952-MigrationName.ts +++ b/Common/Server/Infrastructure/Postgres/SchemaMigrations/1773761409952-MigrationName.ts @@ -1,54 +1,137 @@ import { MigrationInterface, QueryRunner } from "typeorm"; export class MigrationName1773761409952 implements MigrationInterface { - name = 'MigrationName1773761409952' + name = "MigrationName1773761409952"; - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "KubernetesCluster" DROP CONSTRAINT "FK_kubernetes_cluster_projectId"`); - await queryRunner.query(`ALTER TABLE "KubernetesCluster" DROP CONSTRAINT "FK_kubernetes_cluster_createdByUserId"`); - await queryRunner.query(`ALTER TABLE "KubernetesCluster" DROP CONSTRAINT "FK_kubernetes_cluster_deletedByUserId"`); - await queryRunner.query(`ALTER TABLE "KubernetesClusterLabel" DROP CONSTRAINT "FK_kubernetes_cluster_label_clusterId"`); - await queryRunner.query(`ALTER TABLE "KubernetesClusterLabel" DROP CONSTRAINT "FK_kubernetes_cluster_label_labelId"`); - await queryRunner.query(`DROP INDEX "public"."IDX_kubernetes_cluster_projectId"`); - await queryRunner.query(`DROP INDEX "public"."IDX_kubernetes_cluster_clusterIdentifier"`); - await queryRunner.query(`DROP INDEX "public"."IDX_kubernetes_cluster_slug"`); - await queryRunner.query(`DROP INDEX "public"."IDX_kubernetes_cluster_label_clusterId"`); - await queryRunner.query(`DROP INDEX "public"."IDX_kubernetes_cluster_label_labelId"`); - await queryRunner.query(`ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "rotation" SET DEFAULT '{"_type":"Recurring","value":{"intervalType":"Day","intervalCount":{"_type":"PositiveNumber","value":1}}}'`); - await queryRunner.query(`ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "restrictionTimes" SET DEFAULT '{"_type":"RestrictionTimes","value":{"restictionType":"None","dayRestrictionTimes":null,"weeklyRestrictionTimes":[]}}'`); - await queryRunner.query(`CREATE INDEX "IDX_5ae5bbb0c93c048b0b76b1d426" ON "KubernetesCluster" ("projectId") `); - await queryRunner.query(`CREATE INDEX "IDX_b9259f6741a7965a518e258f61" ON "KubernetesCluster" ("clusterIdentifier") `); - await queryRunner.query(`CREATE INDEX "IDX_ed1b53bd041aa21b44ca8cdab5" ON "KubernetesClusterLabel" ("kubernetesClusterId") `); - await queryRunner.query(`CREATE INDEX "IDX_2ec82ad068e84cf762c32ad7c7" ON "KubernetesClusterLabel" ("labelId") `); - await queryRunner.query(`ALTER TABLE "KubernetesCluster" ADD CONSTRAINT "FK_5ae5bbb0c93c048b0b76b1d4268" FOREIGN KEY ("projectId") REFERENCES "Project"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "KubernetesCluster" ADD CONSTRAINT "FK_1bee392c44b1aebe754932133a8" FOREIGN KEY ("createdByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "KubernetesCluster" ADD CONSTRAINT "FK_b0f6c98aac521060f8b68fe5c87" FOREIGN KEY ("deletedByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "KubernetesClusterLabel" ADD CONSTRAINT "FK_ed1b53bd041aa21b44ca8cdab5e" FOREIGN KEY ("kubernetesClusterId") REFERENCES "KubernetesCluster"("_id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "KubernetesClusterLabel" ADD CONSTRAINT "FK_2ec82ad068e84cf762c32ad7c76" FOREIGN KEY ("labelId") REFERENCES "Label"("_id") ON DELETE CASCADE ON UPDATE CASCADE`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE "KubernetesClusterLabel" DROP CONSTRAINT "FK_2ec82ad068e84cf762c32ad7c76"`); - await queryRunner.query(`ALTER TABLE "KubernetesClusterLabel" DROP CONSTRAINT "FK_ed1b53bd041aa21b44ca8cdab5e"`); - await queryRunner.query(`ALTER TABLE "KubernetesCluster" DROP CONSTRAINT "FK_b0f6c98aac521060f8b68fe5c87"`); - await queryRunner.query(`ALTER TABLE "KubernetesCluster" DROP CONSTRAINT "FK_1bee392c44b1aebe754932133a8"`); - await queryRunner.query(`ALTER TABLE "KubernetesCluster" DROP CONSTRAINT "FK_5ae5bbb0c93c048b0b76b1d4268"`); - await queryRunner.query(`DROP INDEX "public"."IDX_2ec82ad068e84cf762c32ad7c7"`); - await queryRunner.query(`DROP INDEX "public"."IDX_ed1b53bd041aa21b44ca8cdab5"`); - await queryRunner.query(`DROP INDEX "public"."IDX_b9259f6741a7965a518e258f61"`); - await queryRunner.query(`DROP INDEX "public"."IDX_5ae5bbb0c93c048b0b76b1d426"`); - await queryRunner.query(`ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "restrictionTimes" SET DEFAULT '{"_type": "RestrictionTimes", "value": {"restictionType": "None", "dayRestrictionTimes": null, "weeklyRestrictionTimes": []}}'`); - await queryRunner.query(`ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "rotation" SET DEFAULT '{"_type": "Recurring", "value": {"intervalType": "Day", "intervalCount": {"_type": "PositiveNumber", "value": 1}}}'`); - await queryRunner.query(`CREATE INDEX "IDX_kubernetes_cluster_label_labelId" ON "KubernetesClusterLabel" ("labelId") `); - await queryRunner.query(`CREATE INDEX "IDX_kubernetes_cluster_label_clusterId" ON "KubernetesClusterLabel" ("kubernetesClusterId") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_kubernetes_cluster_slug" ON "KubernetesCluster" ("slug") `); - await queryRunner.query(`CREATE INDEX "IDX_kubernetes_cluster_clusterIdentifier" ON "KubernetesCluster" ("clusterIdentifier") `); - await queryRunner.query(`CREATE INDEX "IDX_kubernetes_cluster_projectId" ON "KubernetesCluster" ("projectId") `); - await queryRunner.query(`ALTER TABLE "KubernetesClusterLabel" ADD CONSTRAINT "FK_kubernetes_cluster_label_labelId" FOREIGN KEY ("labelId") REFERENCES "Label"("_id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "KubernetesClusterLabel" ADD CONSTRAINT "FK_kubernetes_cluster_label_clusterId" FOREIGN KEY ("kubernetesClusterId") REFERENCES "KubernetesCluster"("_id") ON DELETE CASCADE ON UPDATE CASCADE`); - await queryRunner.query(`ALTER TABLE "KubernetesCluster" ADD CONSTRAINT "FK_kubernetes_cluster_deletedByUserId" FOREIGN KEY ("deletedByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "KubernetesCluster" ADD CONSTRAINT "FK_kubernetes_cluster_createdByUserId" FOREIGN KEY ("createdByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "KubernetesCluster" ADD CONSTRAINT "FK_kubernetes_cluster_projectId" FOREIGN KEY ("projectId") REFERENCES "Project"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "KubernetesCluster" DROP CONSTRAINT "FK_kubernetes_cluster_projectId"`, + ); + await queryRunner.query( + `ALTER TABLE "KubernetesCluster" DROP CONSTRAINT "FK_kubernetes_cluster_createdByUserId"`, + ); + await queryRunner.query( + `ALTER TABLE "KubernetesCluster" DROP CONSTRAINT "FK_kubernetes_cluster_deletedByUserId"`, + ); + await queryRunner.query( + `ALTER TABLE "KubernetesClusterLabel" DROP CONSTRAINT "FK_kubernetes_cluster_label_clusterId"`, + ); + await queryRunner.query( + `ALTER TABLE "KubernetesClusterLabel" DROP CONSTRAINT "FK_kubernetes_cluster_label_labelId"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_kubernetes_cluster_projectId"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_kubernetes_cluster_clusterIdentifier"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_kubernetes_cluster_slug"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_kubernetes_cluster_label_clusterId"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_kubernetes_cluster_label_labelId"`, + ); + await queryRunner.query( + `ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "rotation" SET DEFAULT '{"_type":"Recurring","value":{"intervalType":"Day","intervalCount":{"_type":"PositiveNumber","value":1}}}'`, + ); + await queryRunner.query( + `ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "restrictionTimes" SET DEFAULT '{"_type":"RestrictionTimes","value":{"restictionType":"None","dayRestrictionTimes":null,"weeklyRestrictionTimes":[]}}'`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_5ae5bbb0c93c048b0b76b1d426" ON "KubernetesCluster" ("projectId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_b9259f6741a7965a518e258f61" ON "KubernetesCluster" ("clusterIdentifier") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_ed1b53bd041aa21b44ca8cdab5" ON "KubernetesClusterLabel" ("kubernetesClusterId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_2ec82ad068e84cf762c32ad7c7" ON "KubernetesClusterLabel" ("labelId") `, + ); + await queryRunner.query( + `ALTER TABLE "KubernetesCluster" ADD CONSTRAINT "FK_5ae5bbb0c93c048b0b76b1d4268" FOREIGN KEY ("projectId") REFERENCES "Project"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "KubernetesCluster" ADD CONSTRAINT "FK_1bee392c44b1aebe754932133a8" FOREIGN KEY ("createdByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "KubernetesCluster" ADD CONSTRAINT "FK_b0f6c98aac521060f8b68fe5c87" FOREIGN KEY ("deletedByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "KubernetesClusterLabel" ADD CONSTRAINT "FK_ed1b53bd041aa21b44ca8cdab5e" FOREIGN KEY ("kubernetesClusterId") REFERENCES "KubernetesCluster"("_id") ON DELETE CASCADE ON UPDATE CASCADE`, + ); + await queryRunner.query( + `ALTER TABLE "KubernetesClusterLabel" ADD CONSTRAINT "FK_2ec82ad068e84cf762c32ad7c76" FOREIGN KEY ("labelId") REFERENCES "Label"("_id") ON DELETE CASCADE ON UPDATE CASCADE`, + ); + } + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "KubernetesClusterLabel" DROP CONSTRAINT "FK_2ec82ad068e84cf762c32ad7c76"`, + ); + await queryRunner.query( + `ALTER TABLE "KubernetesClusterLabel" DROP CONSTRAINT "FK_ed1b53bd041aa21b44ca8cdab5e"`, + ); + await queryRunner.query( + `ALTER TABLE "KubernetesCluster" DROP CONSTRAINT "FK_b0f6c98aac521060f8b68fe5c87"`, + ); + await queryRunner.query( + `ALTER TABLE "KubernetesCluster" DROP CONSTRAINT "FK_1bee392c44b1aebe754932133a8"`, + ); + await queryRunner.query( + `ALTER TABLE "KubernetesCluster" DROP CONSTRAINT "FK_5ae5bbb0c93c048b0b76b1d4268"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_2ec82ad068e84cf762c32ad7c7"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_ed1b53bd041aa21b44ca8cdab5"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_b9259f6741a7965a518e258f61"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_5ae5bbb0c93c048b0b76b1d426"`, + ); + await queryRunner.query( + `ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "restrictionTimes" SET DEFAULT '{"_type": "RestrictionTimes", "value": {"restictionType": "None", "dayRestrictionTimes": null, "weeklyRestrictionTimes": []}}'`, + ); + await queryRunner.query( + `ALTER TABLE "OnCallDutyPolicyScheduleLayer" ALTER COLUMN "rotation" SET DEFAULT '{"_type": "Recurring", "value": {"intervalType": "Day", "intervalCount": {"_type": "PositiveNumber", "value": 1}}}'`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_kubernetes_cluster_label_labelId" ON "KubernetesClusterLabel" ("labelId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_kubernetes_cluster_label_clusterId" ON "KubernetesClusterLabel" ("kubernetesClusterId") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_kubernetes_cluster_slug" ON "KubernetesCluster" ("slug") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_kubernetes_cluster_clusterIdentifier" ON "KubernetesCluster" ("clusterIdentifier") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_kubernetes_cluster_projectId" ON "KubernetesCluster" ("projectId") `, + ); + await queryRunner.query( + `ALTER TABLE "KubernetesClusterLabel" ADD CONSTRAINT "FK_kubernetes_cluster_label_labelId" FOREIGN KEY ("labelId") REFERENCES "Label"("_id") ON DELETE CASCADE ON UPDATE CASCADE`, + ); + await queryRunner.query( + `ALTER TABLE "KubernetesClusterLabel" ADD CONSTRAINT "FK_kubernetes_cluster_label_clusterId" FOREIGN KEY ("kubernetesClusterId") REFERENCES "KubernetesCluster"("_id") ON DELETE CASCADE ON UPDATE CASCADE`, + ); + await queryRunner.query( + `ALTER TABLE "KubernetesCluster" ADD CONSTRAINT "FK_kubernetes_cluster_deletedByUserId" FOREIGN KEY ("deletedByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "KubernetesCluster" ADD CONSTRAINT "FK_kubernetes_cluster_createdByUserId" FOREIGN KEY ("createdByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "KubernetesCluster" ADD CONSTRAINT "FK_kubernetes_cluster_projectId" FOREIGN KEY ("projectId") REFERENCES "Project"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + } } diff --git a/Common/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts b/Common/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts index 1719f5dddc..ccd8cd1825 100644 --- a/Common/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +++ b/Common/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts @@ -539,5 +539,5 @@ export default [ MigrationName1773402621107, MigrationName1773676206197, MigrationName1774000000000, - MigrationName1773761409952 + MigrationName1773761409952, ]; diff --git a/Common/Server/Utils/StartServer.ts b/Common/Server/Utils/StartServer.ts index d2b1717dbe..1af018c17b 100644 --- a/Common/Server/Utils/StartServer.ts +++ b/Common/Server/Utils/StartServer.ts @@ -241,7 +241,10 @@ const init: InitFunction = async ( }, ); - app.use(`/${appName}`, ExpressStatic(path.resolve(process.cwd(), "public"))); + app.use( + `/${appName}`, + ExpressStatic(path.resolve(process.cwd(), "public")), + ); app.get( `/${appName}/dist/Index.js`, diff --git a/Common/UI/Components/Markdown.tsx/MarkdownViewer.tsx b/Common/UI/Components/Markdown.tsx/MarkdownViewer.tsx index 72817828c6..9c5788a1fb 100644 --- a/Common/UI/Components/Markdown.tsx/MarkdownViewer.tsx +++ b/Common/UI/Components/Markdown.tsx/MarkdownViewer.tsx @@ -352,9 +352,11 @@ const MarkdownViewer: FunctionComponent = ( return <>{children}; } - // If the child is a custom component (CodeBlock, MermaidDiagram, etc.) - // rather than a plain HTML element like , skip pre styling. - // Checking typeof type !== "string" is minification-safe unlike checking type.name. + /* + * If the child is a custom component (CodeBlock, MermaidDiagram, etc.) + * rather than a plain HTML element like , skip pre styling. + * Checking typeof type !== "string" is minification-safe unlike checking type.name. + */ const isCustomComponent: boolean = React.isValidElement(children) && typeof (children as any).type !== "string"; @@ -439,10 +441,7 @@ const MarkdownViewer: FunctionComponent = ( }, tr: ({ ...props }: any) => { return ( - + ); }, th: ({ ...props }: any) => { @@ -455,10 +454,7 @@ const MarkdownViewer: FunctionComponent = ( }, td: ({ ...props }: any) => { return ( - + ); }, hr: ({ ...props }: any) => { @@ -482,11 +478,11 @@ const MarkdownViewer: FunctionComponent = ( } const isMultiline: boolean = content.includes("\n"); - const hasLanguage: boolean = !!( + const hasLanguage: boolean = Boolean( match && - match?.filter((item: string) => { - return item.includes("language-"); - }).length > 0 + match?.filter((item: string) => { + return item.includes("language-"); + }).length > 0, ); // Multiline code blocks (with or without language) get the full CodeBlock treatment diff --git a/Common/Utils/Traces/CriticalPath.ts b/Common/Utils/Traces/CriticalPath.ts index 847cf194b6..c0aa76d5f3 100644 --- a/Common/Utils/Traces/CriticalPath.ts +++ b/Common/Utils/Traces/CriticalPath.ts @@ -1,5 +1,7 @@ -// Critical Path Analysis for distributed traces -// Computes self-time, critical path, and bottleneck identification +/* + * Critical Path Analysis for distributed traces + * Computes self-time, critical path, and bottleneck identification + */ export interface SpanData { spanId: string; @@ -71,9 +73,10 @@ export default class CriticalPathUtil { selfTimeUnixNano, childTimeUnixNano, totalTimeUnixNano: span.durationUnixNano, - selfTimePercent: span.durationUnixNano > 0 - ? (selfTimeUnixNano / span.durationUnixNano) * 100 - : 0, + selfTimePercent: + span.durationUnixNano > 0 + ? (selfTimeUnixNano / span.durationUnixNano) * 100 + : 0, }); } @@ -110,9 +113,14 @@ export default class CriticalPathUtil { } // Sort by start time - intervals.sort((a: { start: number; end: number }, b: { start: number; end: number }) => { - return a.start - b.start; - }); + intervals.sort( + ( + a: { start: number; end: number }, + b: { start: number; end: number }, + ) => { + return a.start - b.start; + }, + ); // Merge overlapping intervals let mergedDuration: number = 0; @@ -275,9 +283,7 @@ export default class CriticalPathUtil { /** * Compute latency breakdown by service. */ - public static computeServiceBreakdown( - spans: SpanData[], - ): ServiceBreakdown[] { + public static computeServiceBreakdown(spans: SpanData[]): ServiceBreakdown[] { const selfTimes: Map = CriticalPathUtil.computeSelfTimes(spans); @@ -302,12 +308,15 @@ export default class CriticalPathUtil { for (const span of spans) { const serviceId: string = span.serviceId || "unknown"; - const entry: { totalDuration: number; selfTime: number; spanCount: number } = - serviceMap.get(serviceId) || { - totalDuration: 0, - selfTime: 0, - spanCount: 0, - }; + const entry: { + totalDuration: number; + selfTime: number; + spanCount: number; + } = serviceMap.get(serviceId) || { + totalDuration: 0, + selfTime: 0, + spanCount: 0, + }; entry.totalDuration += span.durationUnixNano; const selfTime: SpanSelfTime | undefined = selfTimes.get(span.spanId); @@ -329,11 +338,9 @@ export default class CriticalPathUtil { } // Sort by self-time descending (biggest contributors first) - result.sort( - (a: ServiceBreakdown, b: ServiceBreakdown) => { - return b.selfTimeUnixNano - a.selfTimeUnixNano; - }, - ); + result.sort((a: ServiceBreakdown, b: ServiceBreakdown) => { + return b.selfTimeUnixNano - a.selfTimeUnixNano; + }); return result; } diff --git a/MobileApp/src/components/EmptyState.tsx b/MobileApp/src/components/EmptyState.tsx index 0840d1d461..418b67c401 100644 --- a/MobileApp/src/components/EmptyState.tsx +++ b/MobileApp/src/components/EmptyState.tsx @@ -4,7 +4,13 @@ import { Ionicons } from "@expo/vector-icons"; import { useTheme } from "../theme"; import GradientButton from "./GradientButton"; -type EmptyIcon = "incidents" | "alerts" | "episodes" | "notes" | "monitors" | "default"; +type EmptyIcon = + | "incidents" + | "alerts" + | "episodes" + | "notes" + | "monitors" + | "default"; interface EmptyStateProps { title: string; diff --git a/MobileApp/src/components/MonitorSummaryView.tsx b/MobileApp/src/components/MonitorSummaryView.tsx index f69a952ed7..08a11ca090 100644 --- a/MobileApp/src/components/MonitorSummaryView.tsx +++ b/MobileApp/src/components/MonitorSummaryView.tsx @@ -21,7 +21,10 @@ function toDisplayString(val: unknown): string { if (typeof obj.value === "string") { return obj.value; } - if (typeof obj.toString === "function" && obj.toString !== Object.prototype.toString) { + if ( + typeof obj.toString === "function" && + obj.toString !== Object.prototype.toString + ) { return obj.toString(); } try { diff --git a/MobileApp/src/hooks/useAllProjectCounts.ts b/MobileApp/src/hooks/useAllProjectCounts.ts index 1100000958..7a1c0d2ea3 100644 --- a/MobileApp/src/hooks/useAllProjectCounts.ts +++ b/MobileApp/src/hooks/useAllProjectCounts.ts @@ -142,13 +142,12 @@ export function useAllProjectCounts(): UseAllProjectCountsResult { 0, ); - const inoperationalMonitorCount: number = - inoperationalMonitorQueries.reduce( - (sum: number, q: UseQueryResult, Error>) => { - return sum + (q.data?.count ?? 0); - }, - 0, - ); + const inoperationalMonitorCount: number = inoperationalMonitorQueries.reduce( + (sum: number, q: UseQueryResult, Error>) => { + return sum + (q.data?.count ?? 0); + }, + 0, + ); const isLoading: boolean = incidentQuery.isPending || diff --git a/MobileApp/src/hooks/useAllProjectMonitors.ts b/MobileApp/src/hooks/useAllProjectMonitors.ts index 14e5120f96..926d93eda6 100644 --- a/MobileApp/src/hooks/useAllProjectMonitors.ts +++ b/MobileApp/src/hooks/useAllProjectMonitors.ts @@ -57,8 +57,9 @@ export function useAllProjectMonitors(): UseAllProjectMonitorsResult { const items: ProjectMonitorItem[] = useMemo(() => { const allItems: ProjectMonitorItem[] = []; for (let i: number = 0; i < queries.length; i++) { - const query: UseQueryResult, Error> = - queries[i]!; + const query: UseQueryResult, Error> = queries[ + i + ]!; const projectId: string = projectList[i]?._id ?? ""; if (query.data) { for (const item of query.data.data) { @@ -75,11 +76,9 @@ export function useAllProjectMonitors(): UseAllProjectMonitorsResult { const refetch: () => Promise = async (): Promise => { await Promise.all( - queries.map( - (q: UseQueryResult, Error>) => { - return q.refetch(); - }, - ), + queries.map((q: UseQueryResult, Error>) => { + return q.refetch(); + }), ); }; diff --git a/MobileApp/src/screens/MonitorDetailScreen.tsx b/MobileApp/src/screens/MonitorDetailScreen.tsx index 40756ade86..01f9e192f3 100644 --- a/MobileApp/src/screens/MonitorDetailScreen.tsx +++ b/MobileApp/src/screens/MonitorDetailScreen.tsx @@ -334,7 +334,7 @@ export default function MonitorDetailScreen({ > {isDisabled ? "Disabled" - : (monitor.currentMonitorStatus?.name ?? "Unknown")} + : monitor.currentMonitorStatus?.name ?? "Unknown"} diff --git a/MobileApp/src/screens/MonitorsScreen.tsx b/MobileApp/src/screens/MonitorsScreen.tsx index 7c574d09cf..2df2ef3f3f 100644 --- a/MobileApp/src/screens/MonitorsScreen.tsx +++ b/MobileApp/src/screens/MonitorsScreen.tsx @@ -254,8 +254,7 @@ export default function MonitorsScreen(): React.JSX.Element { for (const wrapped of allMonitors) { const statusName: string = wrapped.item.currentMonitorStatus?.name?.toLowerCase() ?? ""; - const isDisabled: boolean = - wrapped.item.disableActiveMonitoring === true; + const isDisabled: boolean = wrapped.item.disableActiveMonitoring === true; if (isDisabled) { disabledCount++;