DataApi.utils.ts78 lines · main
| 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 | /** |
| 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 | */ |
| 11 | export 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 | |
| 37 | function 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 | |
| 43 | export type EnrichedEntity = { id: string; displayName: string; camelCase: string } |
| 44 | export type EntityMap = Record<string, EnrichedEntity> |
| 45 | |
| 46 | /** |
| 47 | * Partitions JSON schema paths into resource and RPC entity maps. |
| 48 | */ |
| 49 | export 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 | } |