DataApi.utils.ts 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. import type { ProjectJsonSchemaPaths } from '@/data/docs/project-json-schema-query'
  2. import type { LoadBalancer } from '@/data/read-replicas/load-balancers-query'
  3. import type { Database } from '@/data/read-replicas/replicas-query'
  4. import { snakeToCamel } from '@/lib/helpers'
  5. /**
  6. * Resolves the API endpoint URL based on the selected database, custom domain
  7. * status, and load balancer configuration. The returned URL is normalized to
  8. * end with `/rest/v1/` to match the Data API base path documented elsewhere.
  9. */
  10. export function getApiEndpoint({
  11. selectedDatabaseId,
  12. projectRef,
  13. resolvedEndpoint,
  14. loadBalancers,
  15. selectedDatabase,
  16. }: {
  17. selectedDatabaseId: string | undefined
  18. projectRef: string | undefined
  19. resolvedEndpoint: string | undefined
  20. loadBalancers: Array<LoadBalancer> | undefined
  21. selectedDatabase: Database | undefined
  22. }): string {
  23. const loadBalancerSelected = selectedDatabaseId === 'load-balancer'
  24. if (selectedDatabaseId === projectRef && !!resolvedEndpoint) {
  25. return withDataApiPath(resolvedEndpoint)
  26. }
  27. if (loadBalancerSelected) {
  28. return withDataApiPath(loadBalancers?.[0]?.endpoint)
  29. }
  30. return withDataApiPath(selectedDatabase?.restUrl)
  31. }
  32. function withDataApiPath(url: string | undefined): string {
  33. if (!url) return ''
  34. const trimmed = url.replace(/\/+$/, '')
  35. return /\/rest\/v1$/.test(trimmed) ? `${trimmed}/` : `${trimmed}/rest/v1/`
  36. }
  37. export type EnrichedEntity = { id: string; displayName: string; camelCase: string }
  38. export type EntityMap = Record<string, EnrichedEntity>
  39. /**
  40. * Partitions JSON schema paths into resource and RPC entity maps.
  41. */
  42. export function buildEntityMaps(paths: ProjectJsonSchemaPaths | undefined): {
  43. resources: EntityMap
  44. rpcs: EntityMap
  45. } {
  46. const RPC_PREFIX = 'rpc/'
  47. return Object.keys(paths ?? {}).reduce<{ resources: EntityMap; rpcs: EntityMap }>(
  48. (acc, name) => {
  49. const trimmedName = name.slice(1)
  50. if (!trimmedName.length) return acc
  51. const isRpc = trimmedName.startsWith(RPC_PREFIX)
  52. const id = isRpc ? trimmedName.slice(RPC_PREFIX.length) : trimmedName
  53. const enriched: EnrichedEntity = {
  54. id,
  55. displayName: id.replace(/_/g, ' '),
  56. camelCase: snakeToCamel(id),
  57. }
  58. if (isRpc) {
  59. acc.rpcs[id] = enriched
  60. } else {
  61. acc.resources[id] = enriched
  62. }
  63. return acc
  64. },
  65. { resources: {}, rpcs: {} }
  66. )
  67. }