EdgeFunctionOverview.tsx 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. import { PermissionAction } from '@supabase/shared-types/out/constants'
  2. import { IS_PLATFORM, useParams } from 'common'
  3. import { ExternalLink } from 'lucide-react'
  4. import { useRouter } from 'next/router'
  5. import { useEffect, useMemo, useState } from 'react'
  6. import { EdgeFunctionInvocationsSection } from './EdgeFunctionInvocationsSection'
  7. import {
  8. EDGE_FUNCTION_CHART_INTERVALS,
  9. getBucketedTimeRange,
  10. getExecutionMetrics,
  11. getInvocationChartData,
  12. getInvocationTotals,
  13. getInvocationUpdateAnnotation,
  14. getRollingTimeRange,
  15. getUsageMetrics,
  16. toEdgeFunctionChartData,
  17. } from './EdgeFunctionOverview.utils'
  18. import type { EdgeFunctionChartRawDatum } from './EdgeFunctionOverview.utils'
  19. import { EdgeFunctionPerformanceSection } from './EdgeFunctionPerformanceSection'
  20. import { EdgeFunctionRecentErrors } from './EdgeFunctionRecentErrors'
  21. import { EdgeFunctionUsageSection } from './EdgeFunctionUsageSection'
  22. import { useEdgeFunctionOverviewShortcuts } from './useEdgeFunctionOverviewShortcuts'
  23. import { useUnifiedLogsPreview } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
  24. import NoPermission from '@/components/ui/NoPermission'
  25. import {
  26. FunctionsCombinedStatsVariables,
  27. useFunctionsCombinedStatsQuery,
  28. } from '@/data/analytics/functions-combined-stats-query'
  29. import { useEdgeFunctionQuery } from '@/data/edge-functions/edge-function-query'
  30. import { useFillTimeseriesSorted } from '@/hooks/analytics/useFillTimeseriesSorted'
  31. import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
  32. export const EdgeFunctionOverview = () => {
  33. const router = useRouter()
  34. const { ref: projectRef, functionSlug } = useParams()
  35. const { isEnabled: isUnifiedLogsEnabled } = useUnifiedLogsPreview()
  36. const [interval, setInterval] = useState<string>('15min')
  37. const selectedInterval =
  38. EDGE_FUNCTION_CHART_INTERVALS.find((item) => item.key === interval) ||
  39. EDGE_FUNCTION_CHART_INTERVALS[1]
  40. const {
  41. data: selectedFunction,
  42. error: functionError,
  43. isPending: isLoadingFunction,
  44. isError: isErrorFunction,
  45. } = useEdgeFunctionQuery({
  46. projectRef,
  47. slug: functionSlug,
  48. })
  49. const id = selectedFunction?.id
  50. const combinedStatsResults = useFunctionsCombinedStatsQuery(
  51. {
  52. projectRef,
  53. functionId: id,
  54. interval: selectedInterval.key as FunctionsCombinedStatsVariables['interval'],
  55. },
  56. {
  57. enabled: IS_PLATFORM,
  58. }
  59. )
  60. const combinedStatsData = useMemo(
  61. () => (combinedStatsResults.data?.result as EdgeFunctionChartRawDatum[] | undefined) || [],
  62. [combinedStatsResults.data]
  63. )
  64. const [startDate, endDate] = useMemo(
  65. () => getBucketedTimeRange(selectedInterval),
  66. [selectedInterval]
  67. )
  68. const [selectedWindowStart, selectedWindowEnd] = useMemo(
  69. () => getRollingTimeRange(selectedInterval),
  70. [selectedInterval]
  71. )
  72. const dateTimeFormat = selectedInterval.format ?? 'MMM D, h:mma'
  73. const {
  74. data: combinedStatsChartData,
  75. error: combinedStatsError,
  76. isError: isErrorCombinedStats,
  77. } = useFillTimeseriesSorted({
  78. data: combinedStatsData,
  79. timestampKey: 'timestamp',
  80. valueKey: [
  81. 'requests_count',
  82. 'log_count',
  83. 'log_info_count',
  84. 'log_warn_count',
  85. 'log_error_count',
  86. 'success_count',
  87. 'redirect_count',
  88. 'client_err_count',
  89. 'server_err_count',
  90. 'avg_cpu_time_used',
  91. 'avg_memory_used',
  92. 'avg_execution_time',
  93. 'max_execution_time',
  94. 'avg_heap_memory_used',
  95. 'avg_external_memory_used',
  96. 'max_cpu_time_used',
  97. ],
  98. defaultValue: 0,
  99. startDate: startDate.toISOString(),
  100. endDate: endDate.toISOString(),
  101. })
  102. const chartData = useMemo(
  103. () => toEdgeFunctionChartData(combinedStatsChartData),
  104. [combinedStatsChartData]
  105. )
  106. const invocationChartData = useMemo(() => getInvocationChartData(chartData), [chartData])
  107. const { totalInvocationCount, totalWarningCount, totalErrorCount } = useMemo(
  108. () => getInvocationTotals(invocationChartData),
  109. [invocationChartData]
  110. )
  111. const { averageExecutionTime, maxExecutionTime } = useMemo(
  112. () => getExecutionMetrics(chartData),
  113. [chartData]
  114. )
  115. const {
  116. averageCpuTime,
  117. maxCpuTime,
  118. averageMemoryUsage,
  119. totalHeapMemory,
  120. totalExternalMemory,
  121. totalMemoryByType,
  122. } = useMemo(() => getUsageMetrics(chartData), [chartData])
  123. const invocationUpdateAnnotation = useMemo(
  124. () =>
  125. getInvocationUpdateAnnotation({
  126. updatedAt:
  127. selectedFunction?.updated_at === undefined
  128. ? undefined
  129. : String(selectedFunction.updated_at),
  130. invocationChartData,
  131. windowStart: selectedWindowStart,
  132. windowEnd: selectedWindowEnd,
  133. }),
  134. [invocationChartData, selectedFunction?.updated_at, selectedWindowEnd, selectedWindowStart]
  135. )
  136. const invocationActions = useMemo(
  137. () => [
  138. {
  139. label: isUnifiedLogsEnabled ? 'Open logs' : 'Open invocations',
  140. href: `/project/${projectRef}/functions/${functionSlug}/${
  141. isUnifiedLogsEnabled ? 'logs' : 'invocations'
  142. }`,
  143. icon: <ExternalLink size={12} />,
  144. },
  145. ],
  146. [functionSlug, isUnifiedLogsEnabled, projectRef]
  147. )
  148. useEdgeFunctionOverviewShortcuts({
  149. onSetInterval: setInterval,
  150. onRefresh: () => {
  151. combinedStatsResults.refetch()
  152. },
  153. onOpenLogs: () => {
  154. router.push(
  155. `/project/${projectRef}/functions/${functionSlug}/${
  156. isUnifiedLogsEnabled ? 'logs' : 'invocations'
  157. }`
  158. )
  159. },
  160. })
  161. const { isLoading: permissionsLoading, can: canReadFunction } = useAsyncCheckPermissions(
  162. PermissionAction.FUNCTIONS_READ,
  163. functionSlug as string
  164. )
  165. useEffect(() => {
  166. if (!IS_PLATFORM && projectRef && functionSlug) {
  167. router.replace(`/project/${projectRef}/functions/${functionSlug}/details`)
  168. }
  169. }, [functionSlug, projectRef, router])
  170. if (!canReadFunction && !permissionsLoading) {
  171. return <NoPermission isFullPage resourceText="access this edge function" />
  172. }
  173. if (!IS_PLATFORM) {
  174. return null
  175. }
  176. return (
  177. <>
  178. <EdgeFunctionInvocationsSection
  179. interval={interval}
  180. onIntervalChange={setInterval}
  181. selectedInterval={selectedInterval}
  182. actions={invocationActions}
  183. totalInvocationCount={totalInvocationCount}
  184. totalErrorCount={totalErrorCount}
  185. totalWarningCount={totalWarningCount}
  186. isLoadingFunction={isLoadingFunction}
  187. isErrorFunction={isErrorFunction}
  188. functionError={functionError}
  189. isLoadingChart={combinedStatsResults.isLoading}
  190. isErrorChart={isErrorCombinedStats}
  191. chartErrorMessage={combinedStatsError?.message ?? 'Unknown error'}
  192. chartData={invocationChartData}
  193. onChartClick={() => {
  194. router.push(
  195. `/project/${projectRef}/functions/${functionSlug}/${
  196. isUnifiedLogsEnabled ? 'logs' : 'invocations'
  197. }${isUnifiedLogsEnabled ? '' : `?its=${startDate.toISOString()}`}`
  198. )
  199. }}
  200. updateAnnotation={invocationUpdateAnnotation}
  201. />
  202. <EdgeFunctionRecentErrors
  203. functionId={id}
  204. functionSlug={functionSlug as string}
  205. projectRef={projectRef as string}
  206. updatedAt={selectedFunction?.updated_at}
  207. />
  208. <EdgeFunctionPerformanceSection
  209. data={chartData}
  210. dateTimeFormat={dateTimeFormat}
  211. isLoading={combinedStatsResults.isLoading}
  212. isError={isErrorCombinedStats}
  213. errorMessage={combinedStatsError?.message ?? 'Unknown error'}
  214. averageExecutionTime={averageExecutionTime}
  215. maxExecutionTime={maxExecutionTime}
  216. />
  217. <EdgeFunctionUsageSection
  218. data={chartData}
  219. dateTimeFormat={dateTimeFormat}
  220. isLoading={combinedStatsResults.isLoading}
  221. isError={isErrorCombinedStats}
  222. errorMessage={combinedStatsError?.message ?? 'Unknown error'}
  223. averageCpuTime={averageCpuTime}
  224. maxCpuTime={maxCpuTime}
  225. averageMemoryUsage={averageMemoryUsage}
  226. totalHeapMemory={totalHeapMemory}
  227. totalExternalMemory={totalExternalMemory}
  228. totalMemoryByType={totalMemoryByType}
  229. />
  230. </>
  231. )
  232. }
  233. export default EdgeFunctionOverview