From 7664efc41d8b2a6156033cf3b4e02e70ef173cc2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?O=C4=9Fuzhan=20Olguncu?=
<21091016+ogzhanolguncu@users.noreply.github.com>
Date: Thu, 9 Jan 2025 13:45:48 +0300
Subject: [PATCH] =?UTF-8?q?fix:=20correct=20disabled=20stats=20metric=20di?=
=?UTF-8?q?splaying=20valid=20count=20instead=20of=20=E2=80=A6=20(#2796)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* fix: correct disabled stats metric displaying valid count instead of disabled
* fix: maintain the order of verfication requests
---
.../[apiId]/keys/[keyAuthId]/[keyId]/page.tsx | 39 ++++++++---
.../dashboard/components/dashboard/charts.tsx | 65 +++++++++++++++++--
2 files changed, 90 insertions(+), 14 deletions(-)
diff --git a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/page.tsx b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/page.tsx
index f94ecb0bdd..72bfda7652 100644
--- a/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/page.tsx
+++ b/apps/dashboard/app/(app)/apis/[apiId]/keys/[keyAuthId]/[keyId]/page.tsx
@@ -111,6 +111,9 @@ export default async function APIKeyDetailPage(props: {
.then((res) => res.val?.at(0)?.time ?? 0),
]);
+ // Sort all verifications by time first
+ const sortedVerifications = verifications.val!.sort((a, b) => a.time - b.time);
+
const successOverTime: { x: string; y: number }[] = [];
const ratelimitedOverTime: { x: string; y: number }[] = [];
const usageExceededOverTime: { x: string; y: number }[] = [];
@@ -119,30 +122,47 @@ export default async function APIKeyDetailPage(props: {
const expiredOverTime: { x: string; y: number }[] = [];
const forbiddenOverTime: { x: string; y: number }[] = [];
- for (const d of verifications.val!.sort((a, b) => a.time - b.time)) {
+ // Get all unique timestamps
+ const uniqueDates = [...new Set(sortedVerifications.map((d) => d.time))].sort((a, b) => a - b);
+
+ // Ensure each array has entries for all timestamps with zero counts
+ for (const timestamp of uniqueDates) {
+ const x = new Date(timestamp).toISOString();
+ successOverTime.push({ x, y: 0 });
+ ratelimitedOverTime.push({ x, y: 0 });
+ usageExceededOverTime.push({ x, y: 0 });
+ disabledOverTime.push({ x, y: 0 });
+ insufficientPermissionsOverTime.push({ x, y: 0 });
+ expiredOverTime.push({ x, y: 0 });
+ forbiddenOverTime.push({ x, y: 0 });
+ }
+
+ for (const d of sortedVerifications) {
const x = new Date(d.time).toISOString();
+ const index = uniqueDates.indexOf(d.time);
+
switch (d.outcome) {
case "":
case "VALID":
- successOverTime.push({ x, y: d.count });
+ successOverTime[index] = { x, y: d.count };
break;
case "RATE_LIMITED":
- ratelimitedOverTime.push({ x, y: d.count });
+ ratelimitedOverTime[index] = { x, y: d.count };
break;
case "USAGE_EXCEEDED":
- usageExceededOverTime.push({ x, y: d.count });
+ usageExceededOverTime[index] = { x, y: d.count };
break;
case "DISABLED":
- disabledOverTime.push({ x, y: d.count });
+ disabledOverTime[index] = { x, y: d.count };
break;
case "INSUFFICIENT_PERMISSIONS":
- insufficientPermissionsOverTime.push({ x, y: d.count });
+ insufficientPermissionsOverTime[index] = { x, y: d.count };
break;
case "EXPIRED":
- expiredOverTime.push({ x, y: d.count });
+ expiredOverTime[index] = { x, y: d.count };
break;
case "FORBIDDEN":
- forbiddenOverTime.push({ x, y: d.count });
+ forbiddenOverTime[index] = { x, y: d.count };
break;
}
}
@@ -209,6 +229,7 @@ export default async function APIKeyDetailPage(props: {
stats.forbidden += v.count;
}
});
+
const roleTee = key.workspace.roles.map((role) => {
const nested: NestedPermissions = {};
for (const permission of key.workspace.permissions) {
@@ -328,7 +349,7 @@ export default async function APIKeyDetailPage(props: {
-
+
) => {
const { resolvedTheme } = useTheme();
- const colors: { light: Record; dark: Record } = {
+ const colors: {
+ light: Record;
+ dark: Record;
+ } = {
light: {
primary: "#1c1917",
warn: "#FFCD07",
@@ -133,9 +136,9 @@ export const LineChart: React.FC<{
tooltip={{
formatter: (datum) => ({
name: datum.category,
- value: `${Intl.NumberFormat(undefined, { notation: "compact" }).format(
- Number(datum.y),
- )} ms`,
+ value: `${Intl.NumberFormat(undefined, {
+ notation: "compact",
+ }).format(Number(datum.y))} ms`,
}),
}}
/>
@@ -202,6 +205,53 @@ export const StackedColumnChart: React.FC<{
colors: Array;
}> = ({ data, timeGranularity, colors }) => {
const { axisColor } = useColors(colors);
+
+ const formatDate = (date: string) => {
+ const d = new Date(date);
+ if (Number.isNaN(d.getTime())) {
+ return date;
+ }
+
+ switch (timeGranularity) {
+ case "minute":
+ return d.toLocaleString(undefined, {
+ hour: "numeric",
+ minute: "2-digit",
+ hour12: true,
+ month: "short",
+ day: "numeric",
+ });
+ case "hour":
+ return d.toLocaleString(undefined, {
+ hour: "numeric",
+ hour12: true,
+ month: "short",
+ day: "numeric",
+ year: "numeric",
+ });
+ case "day":
+ return d.toLocaleString(undefined, {
+ weekday: "short",
+ month: "short",
+ day: "numeric",
+ year: "numeric",
+ });
+ case "month":
+ return d.toLocaleString(undefined, {
+ month: "long",
+ year: "numeric",
+ });
+ default:
+ return d.toLocaleString(undefined, {
+ month: "short",
+ day: "numeric",
+ year: "numeric",
+ hour: "numeric",
+ minute: "2-digit",
+ });
+ }
+ };
+
return (
({
name: datum.category,
- value: Intl.NumberFormat(undefined, { notation: "compact" }).format(Number(datum.y)),
+ value: Intl.NumberFormat(undefined, {
+ notation: "compact",
+ maximumFractionDigits: 1,
+ compactDisplay: "short",
+ }).format(Number(datum.y)),
}),
}}
/>