mirror of
https://github.com/supabase/supabase.git
synced 2026-10-11 04:15:04 +03:00
feat: add append prop to ReportWidget, add top routes table
This commit is contained in:
1 parent
f3722bd9b7
commit
a2e72a503e
4 files changed
+125
-16
No files matched your search
@@ -15,14 +15,15 @@ export interface ReportWidgetProps<T = any> {
|
||||
tooltip?: string
|
||||
className?: string
|
||||
renderer: (props: ReportWidgetRendererProps) => React.ReactNode
|
||||
expandable?: (props: ReportWidgetRendererProps) => React.ReactNode
|
||||
expandableText?: string
|
||||
append?: (props: ReportWidgetRendererProps) => React.ReactNode
|
||||
// for overriding props, such as data
|
||||
appendProps?: Partial<ReportWidgetRendererProps>
|
||||
// omitting params will hide the "View in logs explorer" button
|
||||
params?: BaseReportParams | LogsEndpointParams
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
export interface ReportWidgetRendererProps extends ReportWidgetProps {
|
||||
export interface ReportWidgetRendererProps<T = any> extends ReportWidgetProps<T> {
|
||||
router: NextRouter
|
||||
projectRef: string
|
||||
}
|
||||
@@ -99,7 +100,7 @@ const ReportWidget: React.FC<ReportWidgetProps> = (props) => {
|
||||
</div>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Portal>
|
||||
</Tooltip.Root>
|
||||
</Tooltip.Root>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -107,16 +108,8 @@ const ReportWidget: React.FC<ReportWidgetProps> = (props) => {
|
||||
{props.data === undefined ? null : props.renderer({ ...props, router, projectRef })}
|
||||
</LoadingOpacity>
|
||||
|
||||
{props.expandable && (
|
||||
<Collapsible>
|
||||
<Collapsible.Trigger asChild>
|
||||
<Button>{props.expandableText || 'Expand'}</Button>
|
||||
</Collapsible.Trigger>
|
||||
<Collapsible.Content>
|
||||
{props.expandable && props.expandable({ ...props, router, projectRef })}
|
||||
</Collapsible.Content>
|
||||
</Collapsible>
|
||||
)}
|
||||
{props.append &&
|
||||
props.append({ ...props, ...(props.appendProps || {}), router, projectRef })}
|
||||
</Panel.Content>
|
||||
</Panel>
|
||||
)
|
||||
|
||||
@@ -74,6 +74,28 @@ export const PRESET_CONFIG: Record<Presets, PresetConfig> = {
|
||||
ORDER BY
|
||||
timestamp ASC`,
|
||||
},
|
||||
topRoutes: {
|
||||
queryType: "logs",
|
||||
sql: (filters)=> `
|
||||
select
|
||||
request.path as path,
|
||||
request.method as method,
|
||||
request.search as search,
|
||||
count(t.id) as count
|
||||
from edge_logs t
|
||||
cross join unnest(metadata) as m
|
||||
cross join unnest(m.response) as response
|
||||
cross join unnest(m.request) as request
|
||||
cross join unnest(request.headers) as headers
|
||||
${generateRexepWhere(filters)}
|
||||
group by
|
||||
request.path, request.method, request.search
|
||||
order by
|
||||
count desc
|
||||
limit
|
||||
5
|
||||
`
|
||||
},
|
||||
errorCounts: {
|
||||
queryType: 'logs',
|
||||
sql: (filters) => `
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import { ReportWidgetProps } from '../ReportWidget'
|
||||
import { ReportWidgetProps, ReportWidgetRendererProps } from '../ReportWidget'
|
||||
import BarChart from 'components/ui/Charts/BarChart'
|
||||
import Table from 'components/to-be-cleaned/Table'
|
||||
import {
|
||||
jsonSyntaxHighlight,
|
||||
TextFormatter,
|
||||
} from 'components/interfaces/Settings/Logs/LogsFormatters'
|
||||
import { Button, Collapsible, IconChevronRight } from 'ui'
|
||||
import { queryParamsToObject } from '../Reports.utils'
|
||||
|
||||
export const renderTotalRequests = (
|
||||
props: ReportWidgetProps<{
|
||||
@@ -24,6 +31,46 @@ export const renderTotalRequests = (
|
||||
)
|
||||
}
|
||||
|
||||
export const renderTopApiRoutes = (
|
||||
props: ReportWidgetRendererProps<{
|
||||
method: string
|
||||
path: string
|
||||
search: string
|
||||
count: number
|
||||
}>
|
||||
) => {
|
||||
const headerClasses = '!text-xs !py-2 p-0 font-bold !bg-scale-400'
|
||||
const cellClasses = '!text-xs !py-2 truncate'
|
||||
return (
|
||||
<>
|
||||
<Table
|
||||
head={
|
||||
<>
|
||||
<Table.th className={headerClasses}>Request</Table.th>
|
||||
<Table.th className={headerClasses + ' text-right'}>Count</Table.th>
|
||||
</>
|
||||
}
|
||||
body={
|
||||
<>
|
||||
{props.data.map((datum) => (
|
||||
<>
|
||||
<Table.tr className="p-0">
|
||||
<Table.td className={[cellClasses].join(' ')}>
|
||||
<RouteTdContent {...datum} />
|
||||
</Table.td>
|
||||
<Table.td className={[cellClasses, 'text-right align-top'].join(' ')}>
|
||||
{datum.count}
|
||||
</Table.td>
|
||||
</Table.tr>
|
||||
</>
|
||||
))}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const renderErrorCounts = (
|
||||
props: ReportWidgetProps<{
|
||||
timestamp: string
|
||||
@@ -74,3 +121,41 @@ export const renderResponseSpeed = (
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
interface RouteTdContentProps {
|
||||
method: string
|
||||
path: string
|
||||
search: string
|
||||
}
|
||||
const RouteTdContent = (datum: RouteTdContentProps) => (
|
||||
<Collapsible>
|
||||
<Collapsible.Trigger asChild>
|
||||
<div className="flex gap-2">
|
||||
<Button type="text" className=" !py-0 !p-1" title="Show more route details">
|
||||
<IconChevronRight
|
||||
size={14}
|
||||
className="transition data-open-parent:rotate-90 data-closed-parent:rotate-0"
|
||||
/>
|
||||
</Button>
|
||||
<TextFormatter className="w-10 h-4 text-center rounded bg-scale-500" value={datum.method} />
|
||||
<div>
|
||||
<TextFormatter className="text-scale-1100" value={datum.path} />
|
||||
<TextFormatter
|
||||
className="max-w-sm text-scale-900"
|
||||
value={decodeURIComponent(datum.search || '')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Collapsible.Trigger>
|
||||
<Collapsible.Content className="pt-2">
|
||||
<pre className={`syntax-highlight overflow-auto rounded bg-scale-300 p-2 !text-xs`}>
|
||||
<div
|
||||
className="text-wrap"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: jsonSyntaxHighlight(queryParamsToObject(datum.search)),
|
||||
}}
|
||||
/>
|
||||
</pre>
|
||||
</Collapsible.Content>
|
||||
</Collapsible>
|
||||
)
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
renderTotalRequests,
|
||||
renderErrorCounts,
|
||||
renderResponseSpeed,
|
||||
renderTopApiRoutes,
|
||||
} from 'components/interfaces/Reports/renderers/ApiRenderers'
|
||||
import { useState, useEffect } from 'react'
|
||||
import ReportHeader from 'components/interfaces/Reports/ReportHeader'
|
||||
@@ -64,6 +65,8 @@ export const ApiReport: NextPageWithLayout = () => {
|
||||
title="Total Requests"
|
||||
data={report.data.totalRequests || []}
|
||||
renderer={renderTotalRequests}
|
||||
append={renderTopApiRoutes}
|
||||
appendProps={{data: report.data.topRoutes || [] }}
|
||||
/>
|
||||
<ReportWidget
|
||||
isLoading={report.isLoading}
|
||||
@@ -94,9 +97,10 @@ const useApiReport = () => {
|
||||
projectRef ?? 'default'
|
||||
)
|
||||
const totalRequests = queryHooks.totalRequests()
|
||||
const topRoutes = queryHooks.topRoutes()
|
||||
const errorCounts = queryHooks.errorCounts()
|
||||
const responseSpeed = queryHooks.responseSpeed()
|
||||
const activeHooks = [totalRequests, errorCounts, responseSpeed]
|
||||
const activeHooks = [totalRequests, topRoutes, errorCounts, responseSpeed]
|
||||
const [filters, setFilters] = useState<ReportFilterItem[]>([])
|
||||
const addFilter = (filter: ReportFilterItem) => {
|
||||
// use a deep equal when comparing objects.
|
||||
@@ -127,6 +131,9 @@ const useApiReport = () => {
|
||||
if (totalRequests[1].changeQuery) {
|
||||
totalRequests[1].changeQuery(PRESET_CONFIG.api.queries.totalRequests.sql(filters))
|
||||
}
|
||||
if (topRoutes[1].changeQuery) {
|
||||
topRoutes[1].changeQuery(PRESET_CONFIG.api.queries.topRoutes.sql(filters))
|
||||
}
|
||||
if (errorCounts[1].changeQuery) {
|
||||
errorCounts[1].changeQuery(PRESET_CONFIG.api.queries.errorCounts.sql(filters))
|
||||
}
|
||||
@@ -151,11 +158,13 @@ const useApiReport = () => {
|
||||
totalRequests: totalRequests[0].logData,
|
||||
errorCounts: errorCounts[0].logData,
|
||||
responseSpeed: responseSpeed[0].logData,
|
||||
topRoutes: topRoutes[0].logData,
|
||||
},
|
||||
params: {
|
||||
totalRequests: totalRequests[0].params,
|
||||
errorCounts: errorCounts[0].params,
|
||||
responseSpeed: responseSpeed[0].params,
|
||||
topRoutes: topRoutes[0].params,
|
||||
},
|
||||
mergeParams: handleSetParams,
|
||||
filters,
|
||||
|
||||
Reference in new issue
Block a user