mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
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.
This commit is contained in:
@@ -149,10 +149,9 @@ const KubernetesDocumentationCard: FunctionComponent<ComponentProps> = (
|
||||
)}
|
||||
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() || "",
|
||||
|
||||
@@ -17,7 +17,9 @@ import MetricsAggregationType from "Common/Types/Metrics/MetricsAggregationType"
|
||||
|
||||
export interface ComponentProps {
|
||||
serviceIds?: Array<ObjectID> | undefined;
|
||||
onFetchSuccess?: ((data: Array<MetricType>, totalCount: number) => void) | undefined;
|
||||
onFetchSuccess?:
|
||||
| ((data: Array<MetricType>, totalCount: number) => void)
|
||||
| undefined;
|
||||
}
|
||||
|
||||
const MetricsTable: FunctionComponent<ComponentProps> = (
|
||||
|
||||
@@ -622,8 +622,7 @@ const SpanViewer: FunctionComponent<ComponentProps> = (
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
{link.attributes &&
|
||||
Object.keys(link.attributes).length > 0 ? (
|
||||
{link.attributes && Object.keys(link.attributes).length > 0 ? (
|
||||
<div>
|
||||
<div className="text-xs text-gray-500 font-medium mb-1">
|
||||
Attributes
|
||||
|
||||
@@ -816,42 +816,39 @@ const TelemetryDocumentation: FunctionComponent<ComponentProps> = (
|
||||
loadIngestionKeys().catch(() => {});
|
||||
}, []);
|
||||
|
||||
const loadIngestionKeys: () => Promise<void> =
|
||||
async (): Promise<void> => {
|
||||
try {
|
||||
setIsLoadingKeys(true);
|
||||
setKeyError("");
|
||||
const result: ListResult<TelemetryIngestionKey> =
|
||||
await ModelAPI.getList<TelemetryIngestionKey>({
|
||||
modelType: TelemetryIngestionKey,
|
||||
query: {
|
||||
projectId: ProjectUtil.getCurrentProjectId()!,
|
||||
},
|
||||
limit: 50,
|
||||
skip: 0,
|
||||
select: {
|
||||
_id: true,
|
||||
name: true,
|
||||
secretKey: true,
|
||||
description: true,
|
||||
},
|
||||
sort: {},
|
||||
});
|
||||
const loadIngestionKeys: () => Promise<void> = async (): Promise<void> => {
|
||||
try {
|
||||
setIsLoadingKeys(true);
|
||||
setKeyError("");
|
||||
const result: ListResult<TelemetryIngestionKey> =
|
||||
await ModelAPI.getList<TelemetryIngestionKey>({
|
||||
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<ComponentProps> = (
|
||||
<div className="w-9 h-9 rounded-full bg-indigo-50 border-2 border-indigo-500 text-indigo-600 flex items-center justify-center text-sm font-bold z-10">
|
||||
{stepNumber}
|
||||
</div>
|
||||
{!isLast && (
|
||||
<div className="w-0.5 flex-1 bg-gray-200 mt-2 mb-0" />
|
||||
)}
|
||||
{!isLast && <div className="w-0.5 flex-1 bg-gray-200 mt-2 mb-0" />}
|
||||
</div>
|
||||
{/* Step content */}
|
||||
<div className={`flex-1 min-w-0 ${isLast ? "pb-0" : "pb-8"}`}>
|
||||
@@ -1023,15 +1018,19 @@ const TelemetryDocumentation: FunctionComponent<ComponentProps> = (
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<div className="flex-1">
|
||||
<Dropdown
|
||||
options={ingestionKeys.map((key: TelemetryIngestionKey): DropdownOption => {
|
||||
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<ComponentProps> = (
|
||||
};
|
||||
})[0]
|
||||
}
|
||||
onChange={(value: DropdownValue | Array<DropdownValue> | null) => {
|
||||
onChange={(
|
||||
value: DropdownValue | Array<DropdownValue> | null,
|
||||
) => {
|
||||
if (value) {
|
||||
setSelectedKeyId(value.toString());
|
||||
}
|
||||
@@ -1195,7 +1196,11 @@ const TelemetryDocumentation: FunctionComponent<ComponentProps> = (
|
||||
{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
|
||||
}.`,
|
||||
<CodeBlock
|
||||
code={installSnippet.code}
|
||||
language={installSnippet.language}
|
||||
@@ -1283,10 +1288,7 @@ const TelemetryDocumentation: FunctionComponent<ComponentProps> = (
|
||||
4,
|
||||
"Run FluentBit",
|
||||
"Start FluentBit with your configuration file.",
|
||||
<CodeBlock
|
||||
code="fluent-bit -c fluent-bit.conf"
|
||||
language="bash"
|
||||
/>,
|
||||
<CodeBlock code="fluent-bit -c fluent-bit.conf" language="bash" />,
|
||||
true,
|
||||
)}
|
||||
</div>
|
||||
@@ -1340,10 +1342,7 @@ const TelemetryDocumentation: FunctionComponent<ComponentProps> = (
|
||||
4,
|
||||
"Run Fluentd",
|
||||
"Start Fluentd with your configuration.",
|
||||
<CodeBlock
|
||||
code="fluentd -c fluentd.conf"
|
||||
language="bash"
|
||||
/>,
|
||||
<CodeBlock code="fluentd -c fluentd.conf" language="bash" />,
|
||||
true,
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -37,7 +37,8 @@ const FlameGraph: FunctionComponent<FlameGraphProps> = (
|
||||
|
||||
const [hoveredSpanId, setHoveredSpanId] = React.useState<string | null>(null);
|
||||
const [focusedSpanId, setFocusedSpanId] = React.useState<string | null>(null);
|
||||
const containerRef: React.RefObject<HTMLDivElement | null> = React.useRef<HTMLDivElement>(null);
|
||||
const containerRef: React.RefObject<HTMLDivElement | null> =
|
||||
React.useRef<HTMLDivElement>(null);
|
||||
|
||||
// Build span data for critical path utility
|
||||
const spanDataList: SpanData[] = React.useMemo(() => {
|
||||
@@ -90,9 +91,7 @@ const FlameGraph: FunctionComponent<FlameGraphProps> = (
|
||||
}
|
||||
}
|
||||
|
||||
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<FlameGraphProps> = (
|
||||
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<FlameGraphProps> = (
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -712,7 +712,13 @@ const TraceExplorer: FunctionComponent<ComponentProps> = (
|
||||
});
|
||||
}
|
||||
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<ComponentProps> = (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setShowCriticalPath(
|
||||
(prev: boolean) => {
|
||||
return !prev;
|
||||
},
|
||||
);
|
||||
setShowCriticalPath((prev: boolean) => {
|
||||
return !prev;
|
||||
});
|
||||
}}
|
||||
className={`text-xs font-medium px-3 py-1.5 rounded-md border transition-all flex items-center space-x-1 ${
|
||||
showCriticalPath
|
||||
@@ -1428,17 +1432,14 @@ const TraceExplorer: FunctionComponent<ComponentProps> = (
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
{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<ComponentProps> = (
|
||||
);
|
||||
|
||||
return (
|
||||
<div key={breakdown.serviceId} className="flex items-center space-x-2">
|
||||
<div
|
||||
key={breakdown.serviceId}
|
||||
className="flex items-center space-x-2"
|
||||
>
|
||||
<span
|
||||
className="h-2.5 w-2.5 rounded-sm ring-1 ring-black/10 flex-shrink-0"
|
||||
style={{
|
||||
@@ -1468,8 +1472,7 @@ const TraceExplorer: FunctionComponent<ComponentProps> = (
|
||||
</div>
|
||||
<span className="text-[10px] text-gray-500 w-20 text-right">
|
||||
{SpanUtil.getSpanDurationAsString({
|
||||
spanDurationInUnixNano:
|
||||
breakdown.selfTimeUnixNano,
|
||||
spanDurationInUnixNano: breakdown.selfTimeUnixNano,
|
||||
divisibilityFactor: divisibilityFactor,
|
||||
})}{" "}
|
||||
({percent.toFixed(1)}%)
|
||||
@@ -1526,9 +1529,7 @@ const TraceExplorer: FunctionComponent<ComponentProps> = (
|
||||
setSelectedSpans([spanId]);
|
||||
}}
|
||||
selectedSpanId={
|
||||
selectedSpans.length > 0
|
||||
? selectedSpans[0]
|
||||
: undefined
|
||||
selectedSpans.length > 0 ? selectedSpans[0] : undefined
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -135,8 +135,10 @@ const TraceServiceMap: FunctionComponent<TraceServiceMapProps> = (
|
||||
);
|
||||
}
|
||||
|
||||
// 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<string, string[]> = new Map();
|
||||
@@ -151,10 +153,7 @@ const TraceServiceMap: FunctionComponent<TraceServiceMapProps> = (
|
||||
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<TraceServiceMapProps> = (
|
||||
refY="3"
|
||||
orient="auto"
|
||||
>
|
||||
<polygon
|
||||
points="0 0, 8 3, 0 6"
|
||||
fill="#9ca3af"
|
||||
/>
|
||||
<polygon points="0 0, 8 3, 0 6" fill="#9ca3af" />
|
||||
</marker>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
{/* 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<TraceServiceMapProps> = (
|
||||
<div
|
||||
key={node.serviceId}
|
||||
className={`absolute rounded-lg border-2 bg-white shadow-sm p-3 ${
|
||||
hasErrors
|
||||
? "border-red-300"
|
||||
: "border-gray-200"
|
||||
hasErrors ? "border-red-300" : "border-gray-200"
|
||||
}`}
|
||||
style={{
|
||||
left: `${pos.x}px`,
|
||||
|
||||
@@ -44,7 +44,9 @@ export interface ComponentProps {
|
||||
spanQuery?: Query<Span> | undefined;
|
||||
isMinimalTable?: boolean | undefined;
|
||||
noItemsMessage?: string | undefined;
|
||||
onFetchSuccess?: ((data: Array<Span>, totalCount: number) => void) | undefined;
|
||||
onFetchSuccess?:
|
||||
| ((data: Array<Span>, totalCount: number) => void)
|
||||
| undefined;
|
||||
}
|
||||
|
||||
const TraceTable: FunctionComponent<ComponentProps> = (
|
||||
@@ -312,12 +314,9 @@ const TraceTable: FunctionComponent<ComponentProps> = (
|
||||
);
|
||||
}
|
||||
return Promise.resolve(
|
||||
RouteUtil.populateRouteParams(
|
||||
RouteMap[PageMap.TRACE_VIEW]!,
|
||||
{
|
||||
modelId: span.traceId!.toString(),
|
||||
},
|
||||
),
|
||||
RouteUtil.populateRouteParams(RouteMap[PageMap.TRACE_VIEW]!, {
|
||||
modelId: span.traceId!.toString(),
|
||||
}),
|
||||
);
|
||||
}}
|
||||
filters={[
|
||||
|
||||
@@ -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<PageComponentProps> = (
|
||||
props: PageComponentProps,
|
||||
@@ -16,17 +20,29 @@ const UnresolvedExceptionsPage: FunctionComponent<PageComponentProps> = (
|
||||
const disableTelemetryForThisProject: boolean =
|
||||
props.currentProject?.reseller?.enableTelemetryFeatures === false;
|
||||
|
||||
const [hasData, setHasData] = useState<boolean | undefined>(undefined);
|
||||
const [serviceCount, setServiceCount] = useState<number | null>(null);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
const [error, setError] = useState<string>("");
|
||||
|
||||
const handleFetchSuccess: (
|
||||
data: Array<TelemetryException>,
|
||||
totalCount: number,
|
||||
) => void = useCallback(
|
||||
(_data: Array<TelemetryException>, totalCount: number) => {
|
||||
setHasData(totalCount > 0);
|
||||
},
|
||||
[],
|
||||
);
|
||||
const fetchServiceCount: PromiseVoidFunction = async (): Promise<void> => {
|
||||
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<PageComponentProps> = (
|
||||
);
|
||||
}
|
||||
|
||||
if (hasData === false) {
|
||||
if (isLoading) {
|
||||
return <PageLoader isVisible={true} />;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <ErrorMessage message={error} />;
|
||||
}
|
||||
|
||||
if (serviceCount === 0) {
|
||||
return <TelemetryDocumentation telemetryType="exceptions" />;
|
||||
}
|
||||
|
||||
@@ -46,7 +70,6 @@ const UnresolvedExceptionsPage: FunctionComponent<PageComponentProps> = (
|
||||
}}
|
||||
title="Unresolved Exceptions"
|
||||
description="All the exceptions that have not been resolved."
|
||||
onFetchSuccess={handleFetchSuccess}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -65,26 +65,25 @@ const KubernetesClusterEvents: FunctionComponent<
|
||||
const endDate: Date = OneUptimeDate.getCurrentDate();
|
||||
const startDate: Date = OneUptimeDate.addRemoveHours(endDate, -24);
|
||||
|
||||
const listResult: ListResult<Log> =
|
||||
await AnalyticsModelAPI.getList<Log>({
|
||||
modelType: Log,
|
||||
query: {
|
||||
projectId: ProjectUtil.getCurrentProjectId()!.toString(),
|
||||
time: new InBetween<Date>(startDate, endDate),
|
||||
},
|
||||
limit: 200,
|
||||
skip: 0,
|
||||
select: {
|
||||
time: true,
|
||||
body: true,
|
||||
severityText: true,
|
||||
attributes: true,
|
||||
},
|
||||
sort: {
|
||||
time: SortOrder.Descending,
|
||||
},
|
||||
requestOptions: {},
|
||||
});
|
||||
const listResult: ListResult<Log> = await AnalyticsModelAPI.getList<Log>({
|
||||
modelType: Log,
|
||||
query: {
|
||||
projectId: ProjectUtil.getCurrentProjectId()!.toString(),
|
||||
time: new InBetween<Date>(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<JSONObject> | undefined;
|
||||
const values = (kvList as JSONObject)["values"] as
|
||||
| Array<JSONObject>
|
||||
| undefined;
|
||||
if (!values) {
|
||||
return "";
|
||||
}
|
||||
@@ -128,7 +129,9 @@ const KubernetesClusterEvents: FunctionComponent<
|
||||
if (!kvList) {
|
||||
return "";
|
||||
}
|
||||
const values = (kvList as JSONObject)["values"] as Array<JSONObject> | undefined;
|
||||
const values = (kvList as JSONObject)["values"] as
|
||||
| Array<JSONObject>
|
||||
| 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<
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="bg-white divide-y divide-gray-200">
|
||||
{events.map(
|
||||
(event: KubernetesEvent, index: number) => {
|
||||
const isWarning: boolean =
|
||||
event.type.toLowerCase() === "warning";
|
||||
return (
|
||||
<tr
|
||||
key={index}
|
||||
className={
|
||||
isWarning ? "bg-yellow-50" : ""
|
||||
}
|
||||
>
|
||||
<td className="px-4 py-3 whitespace-nowrap text-sm text-gray-500">
|
||||
{event.timestamp}
|
||||
</td>
|
||||
<td className="px-4 py-3 whitespace-nowrap text-sm">
|
||||
<span
|
||||
className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${
|
||||
isWarning
|
||||
? "bg-yellow-100 text-yellow-800"
|
||||
: "bg-green-100 text-green-800"
|
||||
}`}
|
||||
>
|
||||
{event.type}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3 whitespace-nowrap text-sm text-gray-900">
|
||||
{event.reason}
|
||||
</td>
|
||||
<td className="px-4 py-3 whitespace-nowrap text-sm text-gray-900">
|
||||
{event.objectKind}/{event.objectName}
|
||||
</td>
|
||||
<td className="px-4 py-3 whitespace-nowrap text-sm text-gray-500">
|
||||
{event.namespace}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm text-gray-500 max-w-md truncate">
|
||||
{event.message}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
},
|
||||
)}
|
||||
{events.map((event: KubernetesEvent, index: number) => {
|
||||
const isWarning: boolean =
|
||||
event.type.toLowerCase() === "warning";
|
||||
return (
|
||||
<tr key={index} className={isWarning ? "bg-yellow-50" : ""}>
|
||||
<td className="px-4 py-3 whitespace-nowrap text-sm text-gray-500">
|
||||
{event.timestamp}
|
||||
</td>
|
||||
<td className="px-4 py-3 whitespace-nowrap text-sm">
|
||||
<span
|
||||
className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${
|
||||
isWarning
|
||||
? "bg-yellow-100 text-yellow-800"
|
||||
: "bg-green-100 text-green-800"
|
||||
}`}
|
||||
>
|
||||
{event.type}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3 whitespace-nowrap text-sm text-gray-900">
|
||||
{event.reason}
|
||||
</td>
|
||||
<td className="px-4 py-3 whitespace-nowrap text-sm text-gray-900">
|
||||
{event.objectKind}/{event.objectName}
|
||||
</td>
|
||||
<td className="px-4 py-3 whitespace-nowrap text-sm text-gray-500">
|
||||
{event.namespace}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm text-gray-500 max-w-md truncate">
|
||||
{event.message}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -205,7 +205,12 @@ const KubernetesClusterPodDetail: FunctionComponent<
|
||||
onChange={(data: MetricViewData) => {
|
||||
setMetricViewData({
|
||||
...data,
|
||||
queryConfigs: [podCpuQuery, podMemoryQuery, cpuQuery, memoryQuery],
|
||||
queryConfigs: [
|
||||
podCpuQuery,
|
||||
podMemoryQuery,
|
||||
cpuQuery,
|
||||
memoryQuery,
|
||||
],
|
||||
formulaConfigs: [],
|
||||
});
|
||||
}}
|
||||
|
||||
@@ -32,9 +32,7 @@ const KubernetesClusterSideMenu: FunctionComponent<ComponentProps> = (
|
||||
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<ComponentProps> = (
|
||||
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 },
|
||||
),
|
||||
}}
|
||||
|
||||
@@ -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<PageComponentProps> = (
|
||||
props: PageComponentProps,
|
||||
@@ -15,15 +20,30 @@ const LogsPage: FunctionComponent<PageComponentProps> = (
|
||||
const disableTelemetryForThisProject: boolean =
|
||||
props.currentProject?.reseller?.enableTelemetryFeatures === false;
|
||||
|
||||
const [hasData, setHasData] = useState<boolean | undefined>(undefined);
|
||||
const [serviceCount, setServiceCount] = useState<number | null>(null);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
const [error, setError] = useState<string>("");
|
||||
const [showDocs, setShowDocs] = useState<boolean>(false);
|
||||
|
||||
const handleCountChange: (count: number) => void = useCallback(
|
||||
(count: number) => {
|
||||
setHasData(count > 0);
|
||||
},
|
||||
[],
|
||||
);
|
||||
const fetchServiceCount: PromiseVoidFunction = async (): Promise<void> => {
|
||||
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<PageComponentProps> = (
|
||||
);
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return <PageLoader isVisible={true} />;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <ErrorMessage message={error} />;
|
||||
}
|
||||
|
||||
if (showDocs) {
|
||||
return (
|
||||
<TelemetryDocumentation
|
||||
@@ -42,7 +70,7 @@ const LogsPage: FunctionComponent<PageComponentProps> = (
|
||||
);
|
||||
}
|
||||
|
||||
if (hasData === false) {
|
||||
if (serviceCount === 0) {
|
||||
return <TelemetryDocumentation telemetryType="logs" />;
|
||||
}
|
||||
|
||||
@@ -53,7 +81,6 @@ const LogsPage: FunctionComponent<PageComponentProps> = (
|
||||
limit={100}
|
||||
enableRealtime={true}
|
||||
id="logs"
|
||||
onCountChange={handleCountChange}
|
||||
onShowDocumentation={() => {
|
||||
setShowDocs(true);
|
||||
}}
|
||||
|
||||
@@ -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<PageComponentProps> = (
|
||||
props: PageComponentProps,
|
||||
@@ -16,17 +20,29 @@ const MetricsPage: FunctionComponent<PageComponentProps> = (
|
||||
const disableTelemetryForThisProject: boolean =
|
||||
props.currentProject?.reseller?.enableTelemetryFeatures === false;
|
||||
|
||||
const [hasData, setHasData] = useState<boolean | undefined>(undefined);
|
||||
const [serviceCount, setServiceCount] = useState<number | null>(null);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
const [error, setError] = useState<string>("");
|
||||
|
||||
const handleFetchSuccess: (
|
||||
data: Array<MetricType>,
|
||||
totalCount: number,
|
||||
) => void = useCallback(
|
||||
(_data: Array<MetricType>, totalCount: number) => {
|
||||
setHasData(totalCount > 0);
|
||||
},
|
||||
[],
|
||||
);
|
||||
const fetchServiceCount: PromiseVoidFunction = async (): Promise<void> => {
|
||||
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<PageComponentProps> = (
|
||||
);
|
||||
}
|
||||
|
||||
if (hasData === false) {
|
||||
if (isLoading) {
|
||||
return <PageLoader isVisible={true} />;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <ErrorMessage message={error} />;
|
||||
}
|
||||
|
||||
if (serviceCount === 0) {
|
||||
return <TelemetryDocumentation telemetryType="metrics" />;
|
||||
}
|
||||
|
||||
return <MetricsTable onFetchSuccess={handleFetchSuccess} />;
|
||||
return <MetricsTable />;
|
||||
};
|
||||
|
||||
export default MetricsPage;
|
||||
|
||||
@@ -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<PageComponentProps> = (
|
||||
props: PageComponentProps,
|
||||
@@ -16,12 +20,29 @@ const TracesPage: FunctionComponent<PageComponentProps> = (
|
||||
const disableTelemetryForThisProject: boolean =
|
||||
props.currentProject?.reseller?.enableTelemetryFeatures === false;
|
||||
|
||||
const [hasData, setHasData] = useState<boolean | undefined>(undefined);
|
||||
const [serviceCount, setServiceCount] = useState<number | null>(null);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
const [error, setError] = useState<string>("");
|
||||
|
||||
const handleFetchSuccess: (data: Array<Span>, totalCount: number) => void =
|
||||
useCallback((_data: Array<Span>, totalCount: number) => {
|
||||
setHasData(totalCount > 0);
|
||||
}, []);
|
||||
const fetchServiceCount: PromiseVoidFunction = async (): Promise<void> => {
|
||||
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<PageComponentProps> = (
|
||||
);
|
||||
}
|
||||
|
||||
if (hasData === false) {
|
||||
if (isLoading) {
|
||||
return <PageLoader isVisible={true} />;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <ErrorMessage message={error} />;
|
||||
}
|
||||
|
||||
if (serviceCount === 0) {
|
||||
return <TelemetryDocumentation telemetryType="traces" />;
|
||||
}
|
||||
|
||||
return <TraceTable onFetchSuccess={handleFetchSuccess} />;
|
||||
return <TraceTable />;
|
||||
};
|
||||
|
||||
export default TracesPage;
|
||||
|
||||
@@ -64,9 +64,7 @@ const ExceptionsRoutes: FunctionComponent<ComponentProps> = (
|
||||
element={
|
||||
<ExceptionsDocumentationPage
|
||||
{...props}
|
||||
pageRoute={
|
||||
RouteMap[PageMap.EXCEPTIONS_DOCUMENTATION] as Route
|
||||
}
|
||||
pageRoute={RouteMap[PageMap.EXCEPTIONS_DOCUMENTATION] as Route}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -61,81 +61,113 @@ const KubernetesRoutes: FunctionComponent<ComponentProps> = (
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteUtil.getLastPathForKey(PageMap.KUBERNETES_CLUSTER_VIEW_PODS)}
|
||||
path={RouteUtil.getLastPathForKey(
|
||||
PageMap.KUBERNETES_CLUSTER_VIEW_PODS,
|
||||
)}
|
||||
element={
|
||||
<KubernetesClusterViewPods
|
||||
{...props}
|
||||
pageRoute={RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_PODS] as Route}
|
||||
pageRoute={
|
||||
RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_PODS] as Route
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteUtil.getLastPathForKey(PageMap.KUBERNETES_CLUSTER_VIEW_POD_DETAIL)}
|
||||
path={RouteUtil.getLastPathForKey(
|
||||
PageMap.KUBERNETES_CLUSTER_VIEW_POD_DETAIL,
|
||||
)}
|
||||
element={
|
||||
<KubernetesClusterViewPodDetail
|
||||
{...props}
|
||||
pageRoute={RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_POD_DETAIL] as Route}
|
||||
pageRoute={
|
||||
RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_POD_DETAIL] as Route
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteUtil.getLastPathForKey(PageMap.KUBERNETES_CLUSTER_VIEW_NODES)}
|
||||
path={RouteUtil.getLastPathForKey(
|
||||
PageMap.KUBERNETES_CLUSTER_VIEW_NODES,
|
||||
)}
|
||||
element={
|
||||
<KubernetesClusterViewNodes
|
||||
{...props}
|
||||
pageRoute={RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_NODES] as Route}
|
||||
pageRoute={
|
||||
RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_NODES] as Route
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteUtil.getLastPathForKey(PageMap.KUBERNETES_CLUSTER_VIEW_NODE_DETAIL)}
|
||||
path={RouteUtil.getLastPathForKey(
|
||||
PageMap.KUBERNETES_CLUSTER_VIEW_NODE_DETAIL,
|
||||
)}
|
||||
element={
|
||||
<KubernetesClusterViewNodeDetail
|
||||
{...props}
|
||||
pageRoute={RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_NODE_DETAIL] as Route}
|
||||
pageRoute={
|
||||
RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_NODE_DETAIL] as Route
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteUtil.getLastPathForKey(PageMap.KUBERNETES_CLUSTER_VIEW_EVENTS)}
|
||||
path={RouteUtil.getLastPathForKey(
|
||||
PageMap.KUBERNETES_CLUSTER_VIEW_EVENTS,
|
||||
)}
|
||||
element={
|
||||
<KubernetesClusterViewEvents
|
||||
{...props}
|
||||
pageRoute={RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_EVENTS] as Route}
|
||||
pageRoute={
|
||||
RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_EVENTS] as Route
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteUtil.getLastPathForKey(PageMap.KUBERNETES_CLUSTER_VIEW_CONTROL_PLANE)}
|
||||
path={RouteUtil.getLastPathForKey(
|
||||
PageMap.KUBERNETES_CLUSTER_VIEW_CONTROL_PLANE,
|
||||
)}
|
||||
element={
|
||||
<KubernetesClusterViewControlPlane
|
||||
{...props}
|
||||
pageRoute={RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_CONTROL_PLANE] as Route}
|
||||
pageRoute={
|
||||
RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_CONTROL_PLANE] as Route
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteUtil.getLastPathForKey(PageMap.KUBERNETES_CLUSTER_VIEW_DELETE)}
|
||||
path={RouteUtil.getLastPathForKey(
|
||||
PageMap.KUBERNETES_CLUSTER_VIEW_DELETE,
|
||||
)}
|
||||
element={
|
||||
<KubernetesClusterViewDelete
|
||||
{...props}
|
||||
pageRoute={RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_DELETE] as Route}
|
||||
pageRoute={
|
||||
RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_DELETE] as Route
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<PageRoute
|
||||
path={RouteUtil.getLastPathForKey(PageMap.KUBERNETES_CLUSTER_VIEW_DOCUMENTATION)}
|
||||
path={RouteUtil.getLastPathForKey(
|
||||
PageMap.KUBERNETES_CLUSTER_VIEW_DOCUMENTATION,
|
||||
)}
|
||||
element={
|
||||
<KubernetesClusterViewDocumentation
|
||||
{...props}
|
||||
pageRoute={RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_DOCUMENTATION] as Route}
|
||||
pageRoute={
|
||||
RouteMap[PageMap.KUBERNETES_CLUSTER_VIEW_DOCUMENTATION] as Route
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -1,54 +1,137 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class MigrationName1773761409952 implements MigrationInterface {
|
||||
name = 'MigrationName1773761409952'
|
||||
name = "MigrationName1773761409952";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -539,5 +539,5 @@ export default [
|
||||
MigrationName1773402621107,
|
||||
MigrationName1773676206197,
|
||||
MigrationName1774000000000,
|
||||
MigrationName1773761409952
|
||||
MigrationName1773761409952,
|
||||
];
|
||||
|
||||
@@ -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`,
|
||||
|
||||
@@ -352,9 +352,11 @@ const MarkdownViewer: FunctionComponent<ComponentProps> = (
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
// If the child is a custom component (CodeBlock, MermaidDiagram, etc.)
|
||||
// rather than a plain HTML element like <code>, 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 <code>, 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<ComponentProps> = (
|
||||
},
|
||||
tr: ({ ...props }: any) => {
|
||||
return (
|
||||
<tr
|
||||
className="hover:bg-gray-50 transition-colors"
|
||||
{...props}
|
||||
/>
|
||||
<tr className="hover:bg-gray-50 transition-colors" {...props} />
|
||||
);
|
||||
},
|
||||
th: ({ ...props }: any) => {
|
||||
@@ -455,10 +454,7 @@ const MarkdownViewer: FunctionComponent<ComponentProps> = (
|
||||
},
|
||||
td: ({ ...props }: any) => {
|
||||
return (
|
||||
<td
|
||||
className="px-4 py-2.5 text-sm text-gray-700"
|
||||
{...props}
|
||||
/>
|
||||
<td className="px-4 py-2.5 text-sm text-gray-700" {...props} />
|
||||
);
|
||||
},
|
||||
hr: ({ ...props }: any) => {
|
||||
@@ -482,11 +478,11 @@ const MarkdownViewer: FunctionComponent<ComponentProps> = (
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -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<string, SpanSelfTime> =
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -142,13 +142,12 @@ export function useAllProjectCounts(): UseAllProjectCountsResult {
|
||||
0,
|
||||
);
|
||||
|
||||
const inoperationalMonitorCount: number =
|
||||
inoperationalMonitorQueries.reduce(
|
||||
(sum: number, q: UseQueryResult<ListResponse<MonitorItem>, Error>) => {
|
||||
return sum + (q.data?.count ?? 0);
|
||||
},
|
||||
0,
|
||||
);
|
||||
const inoperationalMonitorCount: number = inoperationalMonitorQueries.reduce(
|
||||
(sum: number, q: UseQueryResult<ListResponse<MonitorItem>, Error>) => {
|
||||
return sum + (q.data?.count ?? 0);
|
||||
},
|
||||
0,
|
||||
);
|
||||
|
||||
const isLoading: boolean =
|
||||
incidentQuery.isPending ||
|
||||
|
||||
@@ -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<ListResponse<MonitorItem>, Error> =
|
||||
queries[i]!;
|
||||
const query: UseQueryResult<ListResponse<MonitorItem>, 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<void> = async (): Promise<void> => {
|
||||
await Promise.all(
|
||||
queries.map(
|
||||
(q: UseQueryResult<ListResponse<MonitorItem>, Error>) => {
|
||||
return q.refetch();
|
||||
},
|
||||
),
|
||||
queries.map((q: UseQueryResult<ListResponse<MonitorItem>, Error>) => {
|
||||
return q.refetch();
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -334,7 +334,7 @@ export default function MonitorDetailScreen({
|
||||
>
|
||||
{isDisabled
|
||||
? "Disabled"
|
||||
: (monitor.currentMonitorStatus?.name ?? "Unknown")}
|
||||
: monitor.currentMonitorStatus?.name ?? "Unknown"}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
|
||||
@@ -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++;
|
||||
|
||||
Reference in New Issue
Block a user