mirror of
https://github.com/supabase/supabase.git
synced 2026-05-11 19:26:38 +08:00
The Data API overview page (`/integrations/data_api/overview`) was showing the project URL as `https://xxx.supabase.co`, but the documented Data API base URL is `https://xxx.supabase.co/rest/v1/`. This normalizes the URL so it matches the docs. **Changed:** - `getApiEndpoint` now appends `/rest/v1/` to the resolved endpoint (only used by the Data API overview card, so no other dashboard URLs are affected) ## To test - Visit `/dashboard/project/_/integrations/data_api/overview` and confirm the API URL field ends with `/rest/v1/` - Switch the database selector between primary, a read replica, and (if available) a load balancer — all should show a URL ending in `/rest/v1/` - With a custom domain active, the custom domain URL should also end with `/rest/v1/` Addresses [FE-3035](https://linear.app/supabase/issue/FE-3035/dashboard-data-api-page-shows-inconsistent-api-url) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **Bug Fixes** * API endpoints are now properly normalized to ensure consistent path formatting with the `/rest/v1/` suffix across all endpoint sources. * Fixed URL handling for custom domain and load balancer endpoint selection. * Enhanced replica database URL handling to ensure correct trailing slash formatting. * **Tests** * Updated test expectations and added new test cases to verify proper endpoint normalization behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
79 lines
2.4 KiB
TypeScript
79 lines
2.4 KiB
TypeScript
import type { ProjectJsonSchemaPaths } from '@/data/docs/project-json-schema-query'
|
|
import type { LoadBalancer } from '@/data/read-replicas/load-balancers-query'
|
|
import type { Database } from '@/data/read-replicas/replicas-query'
|
|
import { snakeToCamel } from '@/lib/helpers'
|
|
|
|
/**
|
|
* Resolves the API endpoint URL based on the selected database, custom domain
|
|
* status, and load balancer configuration. The returned URL is normalized to
|
|
* end with `/rest/v1/` to match the Data API base path documented elsewhere.
|
|
*/
|
|
export function getApiEndpoint({
|
|
selectedDatabaseId,
|
|
projectRef,
|
|
resolvedEndpoint,
|
|
loadBalancers,
|
|
selectedDatabase,
|
|
}: {
|
|
selectedDatabaseId: string | undefined
|
|
projectRef: string | undefined
|
|
resolvedEndpoint: string | undefined
|
|
loadBalancers: Array<LoadBalancer> | undefined
|
|
selectedDatabase: Database | undefined
|
|
}): string {
|
|
const loadBalancerSelected = selectedDatabaseId === 'load-balancer'
|
|
|
|
if (selectedDatabaseId === projectRef && !!resolvedEndpoint) {
|
|
return withDataApiPath(resolvedEndpoint)
|
|
}
|
|
|
|
if (loadBalancerSelected) {
|
|
return withDataApiPath(loadBalancers?.[0]?.endpoint)
|
|
}
|
|
|
|
return withDataApiPath(selectedDatabase?.restUrl)
|
|
}
|
|
|
|
function withDataApiPath(url: string | undefined): string {
|
|
if (!url) return ''
|
|
const trimmed = url.replace(/\/+$/, '')
|
|
return /\/rest\/v1$/.test(trimmed) ? `${trimmed}/` : `${trimmed}/rest/v1/`
|
|
}
|
|
|
|
export type EnrichedEntity = { id: string; displayName: string; camelCase: string }
|
|
export type EntityMap = Record<string, EnrichedEntity>
|
|
|
|
/**
|
|
* Partitions JSON schema paths into resource and RPC entity maps.
|
|
*/
|
|
export function buildEntityMaps(paths: ProjectJsonSchemaPaths | undefined): {
|
|
resources: EntityMap
|
|
rpcs: EntityMap
|
|
} {
|
|
const RPC_PREFIX = 'rpc/'
|
|
|
|
return Object.keys(paths ?? {}).reduce<{ resources: EntityMap; rpcs: EntityMap }>(
|
|
(acc, name) => {
|
|
const trimmedName = name.slice(1)
|
|
if (!trimmedName.length) return acc
|
|
|
|
const isRpc = trimmedName.startsWith(RPC_PREFIX)
|
|
const id = isRpc ? trimmedName.slice(RPC_PREFIX.length) : trimmedName
|
|
const enriched: EnrichedEntity = {
|
|
id,
|
|
displayName: id.replace(/_/g, ' '),
|
|
camelCase: snakeToCamel(id),
|
|
}
|
|
|
|
if (isRpc) {
|
|
acc.rpcs[id] = enriched
|
|
} else {
|
|
acc.resources[id] = enriched
|
|
}
|
|
|
|
return acc
|
|
},
|
|
{ resources: {}, rpcs: {} }
|
|
)
|
|
}
|