OverviewUsage.constants.ts107 lines · main
| 1 | import { |
| 2 | RawAuthMetricsResponseSchema, |
| 3 | type RawAuthMetricsResponse, |
| 4 | type RawAuthMetricsRow, |
| 5 | } from './OverviewUsage.schema' |
| 6 | import { get } from '@/data/fetchers' |
| 7 | |
| 8 | export type AuthMetricsResponse = RawAuthMetricsResponse |
| 9 | |
| 10 | export type MetricName = |
| 11 | | 'activeUsers' |
| 12 | | 'signUpCount' |
| 13 | | 'apiTotalRequests' |
| 14 | | 'apiErrorRequests' |
| 15 | | 'authTotalRequests' |
| 16 | | 'authTotalErrors' |
| 17 | | 'passwordResetRequests' |
| 18 | |
| 19 | type NumericMetricKey = keyof Omit<RawAuthMetricsRow, 'period'> |
| 20 | |
| 21 | const metricKeyMap: Record<MetricName, NumericMetricKey> = { |
| 22 | activeUsers: 'active_users', |
| 23 | signUpCount: 'sign_up_count', |
| 24 | apiTotalRequests: 'api_total_requests', |
| 25 | apiErrorRequests: 'api_error_requests', |
| 26 | authTotalRequests: 'auth_total_requests', |
| 27 | authTotalErrors: 'auth_total_errors', |
| 28 | passwordResetRequests: 'password_reset_requests', |
| 29 | } |
| 30 | |
| 31 | export const fetchAllAuthMetrics = async (projectRef: string): Promise<RawAuthMetricsResponse> => { |
| 32 | const { data, error } = await get( |
| 33 | `/platform/projects/{ref}/analytics/endpoints/auth.metrics` as any, |
| 34 | { |
| 35 | params: { |
| 36 | path: { ref: projectRef }, |
| 37 | query: { |
| 38 | interval: '1day', |
| 39 | }, |
| 40 | }, |
| 41 | } |
| 42 | ) |
| 43 | if (error) throw error |
| 44 | |
| 45 | const parsed = RawAuthMetricsResponseSchema.safeParse(data) |
| 46 | if (!parsed.success) { |
| 47 | const first = parsed.error.issues[0] |
| 48 | throw new Error(`Invalid auth metrics response: ${first?.message ?? 'Invalid shape'}`) |
| 49 | } |
| 50 | return parsed.data |
| 51 | } |
| 52 | |
| 53 | export const getMetricValues = ( |
| 54 | metrics: AuthMetricsResponse | undefined, |
| 55 | metricName: MetricName |
| 56 | ) => { |
| 57 | const key = metricKeyMap[metricName] |
| 58 | const currentRow = metrics?.result.find((r) => r.period === 'current') |
| 59 | const previousRow = metrics?.result.find((r) => r.period === 'previous') |
| 60 | return { |
| 61 | current: currentRow ? currentRow[key] : 0, |
| 62 | previous: previousRow ? previousRow[key] : 0, |
| 63 | } |
| 64 | } |
| 65 | |
| 66 | export const getApiSuccessRates = (metrics: AuthMetricsResponse | undefined) => { |
| 67 | const { current: apiTotalCurrent, previous: apiTotalPrevious } = getMetricValues( |
| 68 | metrics, |
| 69 | 'apiTotalRequests' |
| 70 | ) |
| 71 | const { current: apiErrorCurrent, previous: apiErrorPrevious } = getMetricValues( |
| 72 | metrics, |
| 73 | 'apiErrorRequests' |
| 74 | ) |
| 75 | const current = |
| 76 | apiTotalCurrent > 0 ? Math.max(0, 100 - (apiErrorCurrent / apiTotalCurrent) * 100) : 0 |
| 77 | const previous = |
| 78 | apiTotalPrevious > 0 ? Math.max(0, 100 - (apiErrorPrevious / apiTotalPrevious) * 100) : 0 |
| 79 | return { current, previous } |
| 80 | } |
| 81 | |
| 82 | export const getAuthSuccessRates = (metrics: AuthMetricsResponse | undefined) => { |
| 83 | const { current: authTotalRequestsCurrent, previous: authTotalRequestsPrevious } = |
| 84 | getMetricValues(metrics, 'authTotalRequests') |
| 85 | const { current: authTotalErrorsCurrent, previous: authTotalErrorsPrevious } = getMetricValues( |
| 86 | metrics, |
| 87 | 'authTotalErrors' |
| 88 | ) |
| 89 | const current = |
| 90 | authTotalRequestsCurrent > 0 |
| 91 | ? Math.max(0, 100 - (authTotalErrorsCurrent / authTotalRequestsCurrent) * 100) |
| 92 | : 0 |
| 93 | const previous = |
| 94 | authTotalRequestsPrevious > 0 |
| 95 | ? Math.max(0, 100 - (authTotalErrorsPrevious / authTotalRequestsPrevious) * 100) |
| 96 | : 0 |
| 97 | return { current, previous } |
| 98 | } |
| 99 | |
| 100 | export const calculatePercentageChange = (current: number, previous: number): number => { |
| 101 | if (previous === 0) return current > 0 ? 100 : 0 |
| 102 | return ((current - previous) / previous) * 100 |
| 103 | } |
| 104 | |
| 105 | export const getChangeColor = (percentageChange: number): string => { |
| 106 | return percentageChange >= 0 ? 'text-brand' : 'text-destructive' |
| 107 | } |