serviceFlowFields.ts 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749
  1. import { BlockFieldConfig } from '../types'
  2. import { getStorageMetadata } from '../utils/storageUtils'
  3. import { formatBytes } from '@/lib/helpers'
  4. // Helper functions that avoid duplication with existing storage utilities
  5. const getFileName = (path: string): string => {
  6. if (!path) return ''
  7. // Remove query parameters and hash
  8. const cleanPath = path.split('?')[0].split('#')[0]
  9. // Get the last part after the last slash
  10. const segments = cleanPath.split('/')
  11. return segments[segments.length - 1] || ''
  12. }
  13. const formatStorageDate = (dateString: string): string => {
  14. try {
  15. return new Date(dateString).toLocaleString()
  16. } catch {
  17. return dateString
  18. }
  19. }
  20. // =============================================================================
  21. // NETWORK FIELDS
  22. // =============================================================================
  23. // Field configurations - using filterable field IDs where possible
  24. export const originFields: BlockFieldConfig[] = [
  25. {
  26. id: 'date', // Matches filterFields 'date' (timerange) - FILTERABLE
  27. label: 'Time',
  28. getValue: (data) => {
  29. if (!data?.timestamp && !data?.date) return null
  30. try {
  31. const timestamp = data?.timestamp || data?.date
  32. return new Date(timestamp).toLocaleString()
  33. } catch {
  34. return 'Invalid date'
  35. }
  36. },
  37. },
  38. ]
  39. // Primary Network Fields (Always Visible) - FILTERABLE
  40. export const networkPrimaryFields: BlockFieldConfig[] = [
  41. {
  42. id: 'host', // Matches filterFields 'host' (input) - FILTERABLE
  43. label: 'Host',
  44. getValue: (data, enrichedData) =>
  45. enrichedData?.request_host || enrichedData?.host || data?.host,
  46. },
  47. {
  48. id: 'method', // Matches filterFields 'method' (checkbox) - FILTERABLE
  49. label: 'Method',
  50. getValue: (data, enrichedData) =>
  51. enrichedData?.request_method || enrichedData?.method || data?.method,
  52. },
  53. {
  54. id: 'pathname', // Matches filterFields 'pathname' (input) - FILTERABLE
  55. label: 'Path',
  56. getValue: (data, enrichedData) =>
  57. enrichedData?.request_path || enrichedData?.pathname || data?.pathname,
  58. },
  59. {
  60. id: 'user_agent',
  61. label: 'Client',
  62. getValue: (_data, enrichedData) => {
  63. const userAgent = enrichedData?.headers_user_agent
  64. if (!userAgent) return null
  65. // TODO: Parse user agent for nice display with icons
  66. return userAgent.length > 50 ? userAgent.substring(0, 50) + '...' : userAgent
  67. },
  68. requiresEnrichedData: true,
  69. },
  70. ]
  71. // Primary API Key Field (Always Visible) - Shows API key type only
  72. export const apiKeyPrimaryField: BlockFieldConfig = {
  73. id: 'api_key_role',
  74. label: 'API Key',
  75. getValue: (_data, enrichedData) => {
  76. const prefix = enrichedData?.api_key_prefix
  77. const jwtRole = enrichedData?.jwt_key_role
  78. const authRole = enrichedData?.authorization_role
  79. if (prefix) {
  80. // Extract key type from prefix
  81. if (prefix.includes('publishable')) return 'publishable'
  82. else if (prefix.includes('secret')) return 'secret'
  83. return 'unknown'
  84. }
  85. // Fallback to JWT apikey postgres role if no API key prefix
  86. if (jwtRole) {
  87. return jwtRole
  88. }
  89. // Fallback to authorization postgres role if no apikey JWT
  90. if (authRole) {
  91. return authRole
  92. }
  93. return null
  94. },
  95. requiresEnrichedData: true,
  96. }
  97. // Additional API Key Fields (Collapsible)
  98. export const apiKeyAdditionalFields: BlockFieldConfig[] = [
  99. {
  100. id: 'api_key_prefix_full',
  101. label: 'API Key Prefix',
  102. getValue: (_data, enrichedData) => {
  103. const prefix = enrichedData?.api_key_prefix
  104. return prefix || null
  105. },
  106. requiresEnrichedData: true,
  107. },
  108. {
  109. id: 'postgres_role',
  110. label: 'Postgres Role',
  111. getValue: (_data, enrichedData) => enrichedData?.jwt_key_role,
  112. requiresEnrichedData: true,
  113. },
  114. {
  115. id: 'api_key_error',
  116. label: 'API Key Error',
  117. getValue: (_data, enrichedData) => enrichedData?.api_key_error,
  118. requiresEnrichedData: true,
  119. },
  120. {
  121. id: 'api_key_hash',
  122. label: 'API Key Hash',
  123. getValue: (_data, enrichedData) => {
  124. const hash = enrichedData?.api_key_hash
  125. return hash ? `${hash.substring(0, 12)}...` : null
  126. },
  127. requiresEnrichedData: true,
  128. },
  129. {
  130. id: 'authorization_role',
  131. label: 'Postgres Role',
  132. getValue: (_data, enrichedData) => enrichedData?.authorization_role,
  133. requiresEnrichedData: true,
  134. },
  135. ]
  136. // Primary User Field (Always Visible)
  137. export const userPrimaryField: BlockFieldConfig = {
  138. id: 'user_id',
  139. label: 'User',
  140. getValue: (_data, enrichedData) => {
  141. const userId = enrichedData?.user_id
  142. return userId ? `${userId.substring(0, 8)}...` : null
  143. },
  144. requiresEnrichedData: true,
  145. }
  146. // Additional User Fields (Collapsible)
  147. export const userAdditionalFields: BlockFieldConfig[] = [
  148. {
  149. id: 'auth_user', // Matches filterFields 'auth_user' (input) - FILTERABLE
  150. label: 'Auth User',
  151. getValue: (data, enrichedData) => enrichedData?.auth_user || data?.auth_user,
  152. },
  153. {
  154. id: 'user_email',
  155. label: 'User Email',
  156. getValue: (_data, enrichedData) => enrichedData?.user_email,
  157. requiresEnrichedData: true,
  158. },
  159. ]
  160. // Primary Location Field (Always Visible)
  161. export const locationPrimaryField: BlockFieldConfig = {
  162. id: 'client_country',
  163. label: 'Location',
  164. getValue: (_data, enrichedData) => {
  165. const country = enrichedData?.client_country || enrichedData?.cf_country
  166. const city = enrichedData?.client_city
  167. if (country && city) return `${city}, ${country}`
  168. if (country) return country
  169. if (city) return city
  170. return null
  171. },
  172. requiresEnrichedData: true,
  173. }
  174. // Additional Location Fields (Collapsible) - includes IP addresses
  175. export const locationAdditionalFields: BlockFieldConfig[] = [
  176. {
  177. id: 'client_continent',
  178. label: 'Continent',
  179. getValue: (_data, enrichedData) => enrichedData?.client_continent,
  180. requiresEnrichedData: true,
  181. },
  182. {
  183. id: 'client_region',
  184. label: 'Region',
  185. getValue: (_data, enrichedData) => enrichedData?.client_region,
  186. requiresEnrichedData: true,
  187. },
  188. {
  189. id: 'client_timezone',
  190. label: 'Timezone',
  191. getValue: (_data, enrichedData) => enrichedData?.client_timezone,
  192. requiresEnrichedData: true,
  193. },
  194. {
  195. id: 'x_real_ip',
  196. label: 'Real IP',
  197. getValue: (_data, enrichedData) => enrichedData?.headers_x_real_ip,
  198. requiresEnrichedData: true,
  199. },
  200. {
  201. id: 'client_ip',
  202. label: 'Client IP',
  203. getValue: (_data, enrichedData) => enrichedData?.client_ip,
  204. requiresEnrichedData: true,
  205. },
  206. ]
  207. // Authorization Fields (Collapsible) - JWT authorization details
  208. export const authorizationFields: BlockFieldConfig[] = [
  209. {
  210. id: 'jwt_auth_key_id',
  211. label: 'Key ID',
  212. getValue: (_data, enrichedData) =>
  213. enrichedData?.jwt_auth_key_id || enrichedData?.jwt_apikey_key_id,
  214. requiresEnrichedData: true,
  215. },
  216. {
  217. id: 'jwt_auth_session_id',
  218. label: 'Session ID',
  219. getValue: (_data, enrichedData) =>
  220. enrichedData?.jwt_auth_session_id || enrichedData?.jwt_apikey_session_id,
  221. requiresEnrichedData: true,
  222. },
  223. {
  224. id: 'jwt_auth_subject',
  225. label: 'Subject',
  226. getValue: (_data, enrichedData) =>
  227. enrichedData?.jwt_auth_subject || enrichedData?.jwt_apikey_subject,
  228. requiresEnrichedData: true,
  229. },
  230. {
  231. id: 'jwt_auth_issuer',
  232. label: 'Issuer',
  233. getValue: (_data, enrichedData) =>
  234. enrichedData?.jwt_auth_issuer || enrichedData?.jwt_apikey_issuer,
  235. requiresEnrichedData: true,
  236. },
  237. {
  238. id: 'jwt_auth_algorithm',
  239. label: 'Algorithm',
  240. getValue: (_data, enrichedData) =>
  241. enrichedData?.jwt_auth_algorithm || enrichedData?.jwt_apikey_algorithm,
  242. requiresEnrichedData: true,
  243. },
  244. {
  245. id: 'jwt_auth_expires_at',
  246. label: 'Expires At',
  247. getValue: (_data, enrichedData) => {
  248. const expiresAt = enrichedData?.jwt_auth_expires_at || enrichedData?.jwt_apikey_expires_at
  249. if (!expiresAt) return null
  250. try {
  251. return new Date(expiresAt * 1000).toLocaleString()
  252. } catch {
  253. return expiresAt
  254. }
  255. },
  256. requiresEnrichedData: true,
  257. },
  258. ]
  259. // Tech Details Fields (Collapsible)
  260. export const techDetailsFields: BlockFieldConfig[] = [
  261. {
  262. id: 'network_protocol',
  263. label: 'Protocol',
  264. getValue: (_data, enrichedData) => enrichedData?.network_protocol,
  265. requiresEnrichedData: true,
  266. },
  267. {
  268. id: 'cf_datacenter',
  269. label: 'Datacenter',
  270. getValue: (_data, enrichedData) =>
  271. enrichedData?.network_datacenter || enrichedData?.cf_datacenter,
  272. requiresEnrichedData: true,
  273. },
  274. {
  275. id: 'cache_status',
  276. label: 'Cache Status',
  277. getValue: (_data, enrichedData) => enrichedData?.response_cache_status,
  278. requiresEnrichedData: true,
  279. },
  280. {
  281. id: 'cf_ray',
  282. label: 'CF-Ray',
  283. getValue: (_data, enrichedData) => enrichedData?.cf_ray,
  284. requiresEnrichedData: true,
  285. },
  286. {
  287. id: 'x_client_info',
  288. label: 'SDK',
  289. getValue: (_data, enrichedData) => enrichedData?.headers_x_client_info,
  290. requiresEnrichedData: true,
  291. },
  292. {
  293. id: 'x_forwarded_proto',
  294. label: 'Forwarded Proto',
  295. getValue: (_data, enrichedData) => enrichedData?.headers_x_forwarded_proto,
  296. requiresEnrichedData: true,
  297. },
  298. ]
  299. // =============================================================================
  300. // POSTGREST FIELDS
  301. // =============================================================================
  302. // Primary PostgREST Fields (Always Visible) - FILTERABLE
  303. export const postgrestPrimaryFields: BlockFieldConfig[] = [
  304. {
  305. id: 'status', // Matches filterFields 'status' (checkbox) - FILTERABLE
  306. label: 'Status',
  307. getValue: (data, enrichedData) => enrichedData?.status || data?.status,
  308. },
  309. {
  310. id: 'postgres_role',
  311. label: 'Postgres Role',
  312. getValue: (_data, enrichedData) => enrichedData?.api_role,
  313. requiresEnrichedData: true,
  314. },
  315. {
  316. id: 'response_time',
  317. label: 'Response Time',
  318. getValue: (data, enrichedData) => {
  319. const time = enrichedData?.response_origin_time || data?.response_time_ms
  320. return time ? `${time}ms` : null
  321. },
  322. requiresEnrichedData: true,
  323. },
  324. ]
  325. // PostgREST Response Details (Collapsible)
  326. export const postgrestResponseFields: BlockFieldConfig[] = [
  327. {
  328. id: 'query_params',
  329. label: 'Query',
  330. getValue: (_data, enrichedData) => enrichedData?.request_search,
  331. requiresEnrichedData: true,
  332. },
  333. {
  334. id: 'content_type',
  335. label: 'Content Type',
  336. getValue: (_data, enrichedData) => enrichedData?.response_content_type,
  337. requiresEnrichedData: true,
  338. },
  339. {
  340. id: 'message',
  341. label: 'Message',
  342. getValue: (_data, enrichedData) => enrichedData?.message,
  343. requiresEnrichedData: true,
  344. },
  345. ]
  346. // =============================================================================
  347. // AUTH FIELDS
  348. // =============================================================================
  349. // Primary GoTrue/Auth Fields (Always Visible)
  350. export const authPrimaryFields: BlockFieldConfig[] = [
  351. {
  352. id: 'auth_path',
  353. label: 'Auth Path',
  354. getValue: (data, enrichedData) => {
  355. return enrichedData?.path || enrichedData?.request_path || data?.path
  356. },
  357. requiresEnrichedData: true,
  358. },
  359. {
  360. id: 'log_id',
  361. label: 'Log ID',
  362. getValue: (data, enrichedData) => {
  363. const logId = data?.id || enrichedData?.id
  364. return logId ? `${logId.substring(0, 8)}...` : null
  365. },
  366. },
  367. {
  368. id: 'referer',
  369. label: 'Referer',
  370. getValue: (_data, enrichedData) => {
  371. return enrichedData?.headers_referer || null
  372. },
  373. requiresEnrichedData: true,
  374. },
  375. ]
  376. // =============================================================================
  377. // EDGE FUNCTION FIELDS
  378. // =============================================================================
  379. // Primary Edge Function Fields (Always Visible)
  380. export const edgeFunctionPrimaryFields: BlockFieldConfig[] = [
  381. {
  382. id: 'status',
  383. label: 'Status',
  384. getValue: (data, enrichedData) => enrichedData?.status || data?.status,
  385. },
  386. {
  387. id: 'function_path',
  388. label: 'Function Path',
  389. getValue: (data, enrichedData) => {
  390. return (
  391. enrichedData?.path || enrichedData?.request_path || data?.path || enrichedData?.request_url
  392. )
  393. },
  394. requiresEnrichedData: false,
  395. },
  396. {
  397. id: 'execution_time',
  398. label: 'Execution Time',
  399. getValue: (data, enrichedData) => {
  400. const time = enrichedData?.execution_time_ms || data?.execution_time_ms
  401. return time ? `${time}ms` : null
  402. },
  403. requiresEnrichedData: false,
  404. },
  405. {
  406. id: 'execution_id',
  407. label: 'Execution ID',
  408. getValue: (data, enrichedData) => {
  409. const execId = enrichedData?.execution_id || data?.execution_id
  410. return execId ? `${execId.substring(0, 8)}...` : null
  411. },
  412. requiresEnrichedData: false,
  413. },
  414. ]
  415. // Edge Function Details (Collapsible)
  416. export const edgeFunctionDetailsFields: BlockFieldConfig[] = [
  417. {
  418. id: 'function_id',
  419. label: 'Function ID',
  420. getValue: (data, enrichedData) => {
  421. const funcId = enrichedData?.function_id || data?.function_id
  422. return funcId ? `${funcId.substring(0, 8)}...` : null
  423. },
  424. requiresEnrichedData: false,
  425. },
  426. {
  427. id: 'execution_region',
  428. label: 'Execution Region',
  429. getValue: (data, enrichedData) => {
  430. return enrichedData?.execution_region || data?.execution_region || null
  431. },
  432. requiresEnrichedData: false,
  433. },
  434. {
  435. id: 'function_log_count',
  436. label: 'Function Logs',
  437. getValue: (data, enrichedData) => {
  438. const count = enrichedData?.function_log_count || data?.function_log_count || data?.log_count
  439. return count ? `${count} logs` : 'No logs'
  440. },
  441. requiresEnrichedData: false,
  442. },
  443. {
  444. id: 'method',
  445. label: 'Method',
  446. getValue: (data, enrichedData) => enrichedData?.method || data?.method,
  447. requiresEnrichedData: false,
  448. },
  449. {
  450. id: 'user_agent',
  451. label: 'User Agent',
  452. getValue: (data, enrichedData) => {
  453. const ua = enrichedData?.headers_user_agent || data?.headers_user_agent
  454. return ua ? (ua.length > 30 ? `${ua.substring(0, 30)}...` : ua) : null
  455. },
  456. requiresEnrichedData: false,
  457. },
  458. ]
  459. // =============================================================================
  460. // STORAGE FIELDS
  461. // =============================================================================
  462. // Primary Storage Fields (Always Visible)
  463. export const storagePrimaryFields: BlockFieldConfig[] = [
  464. {
  465. id: 'status',
  466. label: 'Status',
  467. getValue: (data, enrichedData) => enrichedData?.status || data?.status,
  468. },
  469. {
  470. id: 'filename',
  471. label: 'File Name',
  472. getValue: (data, enrichedData) => {
  473. const path = enrichedData?.path || enrichedData?.request_path || data?.path
  474. return path ? getFileName(path) : null
  475. },
  476. requiresEnrichedData: false,
  477. },
  478. {
  479. id: 'content_type_size',
  480. label: 'Type & Size',
  481. getValue: (data, enrichedData) => {
  482. const status = enrichedData?.status || data?.status
  483. const isObjectDeleted = status === 404 || status === '404'
  484. const hasError = status && Number(status) >= 400
  485. // For deleted objects, show status message
  486. if (isObjectDeleted) {
  487. return 'Object deleted'
  488. } else if (hasError) {
  489. return `Error ${status}`
  490. }
  491. // Try to get metadata like PreviewPane does
  492. const metadata = getStorageMetadata(data, enrichedData)
  493. const mimeType = metadata?.mimetype
  494. const size = metadata?.size
  495. // Fallback to headers if metadata not available
  496. const contentType =
  497. mimeType ||
  498. enrichedData?.response_content_type ||
  499. enrichedData?.storage_request_content_type
  500. const contentLength =
  501. size ||
  502. (enrichedData?.storage_content_length
  503. ? parseInt(enrichedData.storage_content_length)
  504. : null)
  505. if (contentType && contentLength) {
  506. return `${contentType} - ${formatBytes(contentLength)}`
  507. } else if (contentType) {
  508. return contentType
  509. } else if (contentLength) {
  510. return formatBytes(contentLength)
  511. }
  512. return 'Unknown type'
  513. },
  514. requiresEnrichedData: true,
  515. },
  516. {
  517. id: 'response_time',
  518. label: 'Response Time',
  519. getValue: (data, enrichedData) => {
  520. const time = enrichedData?.response_origin_time || data?.response_time_ms
  521. return time ? `${time}ms` : null
  522. },
  523. requiresEnrichedData: true,
  524. },
  525. ]
  526. // Storage Details (Collapsible)
  527. export const storageDetailsFields: BlockFieldConfig[] = [
  528. {
  529. id: 'storage_path',
  530. label: 'Storage Path',
  531. getValue: (data, enrichedData) => {
  532. const path = enrichedData?.path || enrichedData?.request_path || data?.path
  533. // Extract just the object path from the full storage path
  534. const match = path?.match(/\/storage\/v1\/object\/([^\/]+)\/(.+)/)
  535. if (match) {
  536. const [, bucketName, objectPath] = match
  537. return `${bucketName}/${objectPath}`
  538. }
  539. return path
  540. },
  541. requiresEnrichedData: false,
  542. },
  543. {
  544. id: 'last_modified',
  545. label: 'Last Modified',
  546. getValue: (data, enrichedData) => {
  547. const status = enrichedData?.status || data?.status
  548. const isObjectDeleted = status === 404 || status === '404'
  549. const hasError = status && Number(status) >= 400
  550. if (isObjectDeleted) {
  551. return 'Object deleted'
  552. } else if (hasError) {
  553. return 'Unavailable'
  554. }
  555. // Try to get dates from metadata like PreviewPane does
  556. const metadata = getStorageMetadata(data, enrichedData)
  557. const updatedAt = metadata?.updated_at || data?.updated_at || enrichedData?.updated_at
  558. const lastModified = enrichedData?.storage_last_modified || updatedAt
  559. return lastModified ? formatStorageDate(lastModified) : null
  560. },
  561. requiresEnrichedData: true,
  562. },
  563. {
  564. id: 'etag',
  565. label: 'ETag',
  566. getValue: (data, enrichedData) => {
  567. const status = enrichedData?.status || data?.status
  568. const isObjectDeleted = status === 404 || status === '404'
  569. const hasError = status && Number(status) >= 400
  570. if (isObjectDeleted) {
  571. return 'Object deleted'
  572. } else if (hasError) {
  573. return 'Unavailable'
  574. }
  575. const etag = enrichedData?.storage_etag
  576. return etag ? `${etag.substring(0, 12)}...` : null
  577. },
  578. requiresEnrichedData: true,
  579. },
  580. {
  581. id: 'content_disposition',
  582. label: 'Content Disposition',
  583. getValue: (data, enrichedData) => {
  584. const status = enrichedData?.status || data?.status
  585. const isObjectDeleted = status === 404 || status === '404'
  586. const hasError = status && Number(status) >= 400
  587. if (isObjectDeleted) {
  588. return 'Object deleted'
  589. } else if (hasError) {
  590. return 'Unavailable'
  591. }
  592. return enrichedData?.storage_content_disposition || null
  593. },
  594. requiresEnrichedData: true,
  595. },
  596. {
  597. id: 'method',
  598. label: 'Method',
  599. getValue: (data, enrichedData) => enrichedData?.method || data?.method,
  600. requiresEnrichedData: false,
  601. },
  602. {
  603. id: 'user_agent',
  604. label: 'User Agent',
  605. getValue: (data, enrichedData) => {
  606. const ua = enrichedData?.headers_user_agent || data?.headers_user_agent
  607. return ua ? (ua.length > 30 ? `${ua.substring(0, 30)}...` : ua) : null
  608. },
  609. requiresEnrichedData: false,
  610. },
  611. ]
  612. // =============================================================================
  613. // POSTGRES FIELDS
  614. // =============================================================================
  615. // Primary Postgres Fields (Always Visible)
  616. export const postgresPrimaryFields: BlockFieldConfig[] = [
  617. {
  618. id: 'event_message',
  619. label: 'Message',
  620. getValue: (data, enrichedData) => enrichedData?.event_message || data?.event_message,
  621. wrap: true,
  622. },
  623. {
  624. id: 'status',
  625. label: 'Status',
  626. getValue: (data, enrichedData) => enrichedData?.status || data?.status,
  627. },
  628. {
  629. id: 'command_tag',
  630. label: 'Command',
  631. getValue: (data, enrichedData) => enrichedData?.command_tag || data?.command_tag,
  632. requiresEnrichedData: true,
  633. },
  634. {
  635. id: 'database_name',
  636. label: 'Database',
  637. getValue: (data, enrichedData) => enrichedData?.database_name || data?.database_name,
  638. requiresEnrichedData: true,
  639. },
  640. {
  641. id: 'database_user',
  642. label: 'User',
  643. getValue: (data, enrichedData) => enrichedData?.database_user || data?.database_user,
  644. requiresEnrichedData: true,
  645. },
  646. ]
  647. // Postgres Details (Collapsible)
  648. export const postgresDetailsFields: BlockFieldConfig[] = [
  649. {
  650. id: 'backend_type',
  651. label: 'Backend Type',
  652. getValue: (data, enrichedData) => enrichedData?.backend_type || data?.backend_type,
  653. requiresEnrichedData: true,
  654. },
  655. {
  656. id: 'connection_from',
  657. label: 'Connection From',
  658. getValue: (data, enrichedData) => enrichedData?.connection_from || data?.connection_from,
  659. requiresEnrichedData: true,
  660. },
  661. {
  662. id: 'session_id',
  663. label: 'Session ID',
  664. getValue: (data, enrichedData) => enrichedData?.session_id || data?.session_id,
  665. requiresEnrichedData: true,
  666. },
  667. {
  668. id: 'process_id',
  669. label: 'Process ID',
  670. getValue: (data, enrichedData) => enrichedData?.process_id || data?.process_id,
  671. requiresEnrichedData: true,
  672. },
  673. {
  674. id: 'query_id',
  675. label: 'Query ID',
  676. getValue: (data, enrichedData) => {
  677. const queryId = enrichedData?.query_id || data?.query_id
  678. return queryId ? String(queryId) : null
  679. },
  680. requiresEnrichedData: true,
  681. },
  682. {
  683. id: 'transaction_id',
  684. label: 'Transaction ID',
  685. getValue: (data, enrichedData) => {
  686. const txId = enrichedData?.transaction_id || data?.transaction_id
  687. return txId ? String(txId) : null
  688. },
  689. requiresEnrichedData: true,
  690. },
  691. {
  692. id: 'virtual_transaction_id',
  693. label: 'Virtual TX ID',
  694. getValue: (data, enrichedData) =>
  695. enrichedData?.virtual_transaction_id || data?.virtual_transaction_id,
  696. requiresEnrichedData: true,
  697. },
  698. {
  699. id: 'session_start_time',
  700. label: 'Session Started',
  701. getValue: (data, enrichedData) => {
  702. const startTime = enrichedData?.session_start_time || data?.session_start_time
  703. return startTime ? new Date(startTime).toLocaleString() : null
  704. },
  705. requiresEnrichedData: true,
  706. },
  707. ]