| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- import dayjs from 'dayjs'
- import {
- type BaseQueries,
- type PresetConfig,
- type ReportFilterItem,
- type ReportQuery,
- } from './Reports.types'
- import {
- isUnixMicro,
- unixMicroToIsoTimestamp,
- } from '@/components/interfaces/Settings/Logs/Logs.utils'
- import { REPORT_STATUS_CODE_COLORS } from '@/data/reports/report.utils'
- import useDbQuery, { DbQueryHook } from '@/hooks/analytics/useDbQuery'
- import useLogsQuery, { LogsQueryHook } from '@/hooks/analytics/useLogsQuery'
- import { getHttpStatusCodeInfo } from '@/lib/http-status-codes'
- /**
- * Converts a query params string to an object
- */
- export const queryParamsToObject = (params: string) => {
- return Object.fromEntries(new URLSearchParams(params))
- }
- export type PresetHookResult = LogsQueryHook | DbQueryHook
- type PresetHooks = Record<keyof PresetConfig['queries'], () => PresetHookResult>
- /**
- * @deprecated
- * Queries are hooks, avoid generating hooks dynamically
- * Generate fetch functions instead, and pass it to a hook inside the component
- */
- export const queriesFactory = <T extends string>(
- queries: BaseQueries<T>,
- projectRef: string
- ): PresetHooks => {
- const hooks: PresetHooks = Object.entries<ReportQuery>(queries).reduce((acc, [k, query]) => {
- if (query.queryType === 'db') {
- return {
- ...acc,
- [k]: () => useDbQuery({ sql: query.safeSql }),
- }
- } else {
- return {
- ...acc,
- [k]: () => useLogsQuery(projectRef),
- }
- }
- }, {})
- return hooks
- }
- export function getLogsSql(query: ReportQuery, filters: ReportFilterItem[]): string {
- if (query.queryType !== 'logs') {
- throw new Error(`Expected logs query, got ${query.queryType}`)
- }
- return query.sql(filters)
- }
- /**
- * Formats a timestamp to a human readable format in UTC
- *
- * @param timestamp - The timestamp to format
- * @param returnUtc - Whether to return the timestamp in UTC
- * @param format - The format to use for the timestamp
- * @returns The formatted timestamp string
- */
- export const formatTimestamp = (
- timestamp: number | string,
- { returnUtc = false, format = 'MMM D, h:mma' }: { returnUtc?: boolean; format?: string } = {}
- ) => {
- try {
- const isSeconds = String(timestamp).length === 10
- const isMicroseconds = String(timestamp).length === 16
- const timestampInMs = isSeconds
- ? Number(timestamp) * 1000
- : isMicroseconds
- ? Number(timestamp) / 1000
- : Number(timestamp)
- if (returnUtc) {
- return dayjs.utc(timestampInMs).format(format)
- } else {
- return dayjs(timestampInMs).format(format)
- }
- } catch (error) {
- console.error(error)
- return 'Invalid Date'
- }
- }
- /**
- * Extracts distinct status codes from log data rows
- */
- export function extractStatusCodesFromData(data: any[]): string[] {
- const statusCodes = new Set<string>()
- data.forEach((item: any) => {
- if (item.status_code !== undefined && item.status_code !== null) {
- statusCodes.add(String(item.status_code))
- }
- })
- return Array.from(statusCodes).sort()
- }
- /**
- * Generates chart attributes for status codes with labels and colors
- */
- export function generateStatusCodeAttributes(statusCodes: string[]) {
- return statusCodes.map((code) => ({
- attribute: code,
- label: `${code} ${getHttpStatusCodeInfo(parseInt(code, 10)).label}`,
- color: REPORT_STATUS_CODE_COLORS[code] || REPORT_STATUS_CODE_COLORS.default,
- }))
- }
- /**
- * Pivots rows of { timestamp, status_code, count } into { timestamp, [status_code]: count }
- * and normalizes timestamps to ISO strings (UTC), filling missing codes with 0 per timestamp
- */
- export function transformStatusCodeData(data: any[], statusCodes: string[]) {
- const pivotedData = data.reduce((acc: Record<string, any>, d: any) => {
- const timestamp = isUnixMicro(d.timestamp)
- ? unixMicroToIsoTimestamp(d.timestamp)
- : dayjs.utc(d.timestamp).toISOString()
- if (!acc[timestamp]) {
- acc[timestamp] = { timestamp }
- statusCodes.forEach((code) => {
- acc[timestamp][code] = 0
- })
- }
- const codeKey = String(d.status_code)
- if (codeKey in acc[timestamp]) {
- acc[timestamp][codeKey] = d.count
- }
- return acc
- }, {})
- return Object.values(pivotedData)
- }
- /**
- * Extract distinct string values for a given field from data rows
- */
- export function extractDistinctValuesFromData(data: any[], field: string): string[] {
- const values = new Set<string>()
- data.forEach((item: any) => {
- if (item[field] !== undefined && item[field] !== null) {
- values.add(String(item[field]))
- }
- })
- return Array.from(values).sort()
- }
- /**
- * Generates chart attributes from a list of category values
- */
- export function generateCategoryAttributes(
- values: string[],
- labelResolver?: (v: string) => string
- ) {
- return values.map((v) => ({
- attribute: v,
- label: labelResolver ? labelResolver(v) : v,
- }))
- }
- /**
- * Pivot rows of { timestamp, [categoryField], count } into { timestamp, [category]: count }
- */
- export function transformCategoricalCountData(
- data: any[],
- categoryField: string,
- categories: string[]
- ) {
- const pivotedData = data.reduce((acc: Record<string, any>, d: any) => {
- const timestamp = isUnixMicro(d.timestamp)
- ? unixMicroToIsoTimestamp(d.timestamp)
- : dayjs.utc(d.timestamp).toISOString()
- if (!acc[timestamp]) {
- acc[timestamp] = { timestamp }
- categories.forEach((c) => {
- acc[timestamp][c] = 0
- })
- }
- const key = String(d[categoryField])
- if (key in acc[timestamp]) {
- acc[timestamp][key] = d.count
- }
- return acc
- }, {})
- return Object.values(pivotedData)
- }
|