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