chore: consolidate useAPIKeysQuery + getKeys into a single useAPIKeys hook (#46761)

## Problem

- API may return a non-array shape that can crash `getKeys` because of
an hard coded cast
- getting API keys is cumbersome as consumers have to call two functions

## Solution

- consolidate `useAPIKeysQuery` + `getKeys` into a single `useAPIKeys`
hook
- guard `getKeys` so that it doesn't crash if passed a non array value
- update usages

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Refactor**
* Unified how project API keys are retrieved across the studio,
resulting in more consistent loading/error handling and slight
responsiveness improvements when showing keys and related command
snippets. UI and permissions behavior remain unchanged for end users.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Gildas Garcia authored and GitHub committed 2026-06-09 15:49:10 +02:00
1 parent f622bc6ea0
commit 43300d43ce
18 files changed
+93 -62

No files matched your search

@@ -15,7 +15,7 @@ import {
import { COMMAND_MENU_SECTIONS } from './CommandMenu.utils'
import { orderCommandSectionsByPriority } from './ordering'
import { getKeys, useAPIKeysQuery } from '@/data/api-keys/api-keys-query'
import { useAPIKeys } from '@/data/api-keys/api-keys-query'
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
@@ -31,13 +31,13 @@ export function useApiKeysCommands() {
const { can: canReadAPIKeys } = useAsyncCheckPermissions(PermissionAction.SECRETS_READ, '*')
const { data: apiKeys } = useAPIKeysQuery(
const { data: apiKeysData } = useAPIKeys(
{ projectRef: project?.ref, reveal: true },
{ enabled: canReadAPIKeys }
)
const commands = useMemo(() => {
const { anonKey, serviceKey, publishableKey, allSecretKeys } = canReadAPIKeys
? getKeys(apiKeys)
? (apiKeysData ?? {})
: {}
return [
@@ -127,7 +127,7 @@ export function useApiKeysCommands() {
icon: () => <Key />,
},
].filter(Boolean) as ICommand[]
}, [apiKeys, canReadAPIKeys, project, ref, resetCommandMenu, setIsOpen])
}, [canReadAPIKeys, apiKeysData, project, ref, resetCommandMenu, setIsOpen])
useRegisterPage(
API_KEYS_PAGE_NAME,
@@ -12,7 +12,7 @@ import { useAvailableConnectModes } from './useAvailableConnectModes'
import { useConnectSheetParams } from './useConnectSheetParams'
import { useConnectSheetShortcut } from './useConnectSheetShortcut'
import { useConnectState } from './useConnectState'
import { getKeys, useAPIKeysQuery } from '@/data/api-keys/api-keys-query'
import { useAPIKeys } from '@/data/api-keys/api-keys-query'
import { useProjectApiUrl } from '@/data/config/project-endpoint-query'
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
import { useTrack } from '@/lib/telemetry/track'
@@ -159,18 +159,16 @@ export const ConnectSheet = () => {
PermissionAction.READ,
'service_api_keys'
)
const { data: apiKeys } = useAPIKeysQuery({ projectRef }, { enabled: canReadAPIKeys })
const { anonKey, publishableKey } = canReadAPIKeys
? getKeys(apiKeys)
: { anonKey: null, publishableKey: null }
const { data: apiKeysData } = useAPIKeys({ projectRef }, { enabled: canReadAPIKeys })
const projectKeys: ProjectKeys = useMemo(() => {
const { anonKey, publishableKey } = apiKeysData ?? {}
return {
apiUrl: endpoint,
anonKey: anonKey?.api_key ?? null,
publishableKey: publishableKey?.api_key ?? null,
}
}, [endpoint, anonKey?.api_key, publishableKey?.api_key])
}, [endpoint, apiKeysData])
const availableModes = useMemo(
() => schema.modes.filter((m) => availableModeIds.includes(m.id)),
@@ -11,7 +11,7 @@ import {
FormSectionContent,
FormSectionLabel,
} from '@/components/ui/Forms/FormSection'
import { getKeys, useAPIKeysQuery } from '@/data/api-keys/api-keys-query'
import { useAPIKeys } from '@/data/api-keys/api-keys-query'
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
import { uuidv4 } from '@/lib/helpers'
@@ -23,12 +23,14 @@ export const HTTPHeaders = ({ form }: HTTPHeadersProps) => {
const { ref } = useParams()
const { can: canReadAPIKeys } = useAsyncCheckPermissions(PermissionAction.SECRETS_READ, '*')
const { data: apiKeys } = useAPIKeysQuery(
{ projectRef: ref, reveal: true },
const { data: apiKeyData } = useAPIKeys(
{
projectRef: ref,
reveal: true,
},
{ enabled: canReadAPIKeys }
)
const { serviceKey, secretKey } = getKeys(apiKeys)
const { serviceKey, secretKey } = apiKeyData ?? {}
const apiKey = secretKey?.api_key ?? serviceKey?.api_key ?? '[YOUR API KEY]'
const functionType = useWatch({ control: form.control, name: 'function_type' })
@@ -24,7 +24,7 @@ import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import { CREATE_NEW_KEY, CREATE_NEW_NAMESPACE } from './DestinationForm.constants'
import type { DestinationPanelSchemaType } from './DestinationForm.schema'
import { InlineLink } from '@/components/ui/InlineLink'
import { getKeys, useAPIKeysQuery } from '@/data/api-keys/api-keys-query'
import { useAPIKeys } from '@/data/api-keys/api-keys-query'
import { useAnalyticsBucketsQuery } from '@/data/storage/analytics-buckets-query'
import { useIcebergNamespacesQuery } from '@/data/storage/iceberg-namespaces-query'
import { useStorageCredentialsQuery } from '@/data/storage/s3-access-key-query'
@@ -370,11 +370,11 @@ export const AnalyticsBucketFields = ({
const { ref: projectRef } = useParams()
const { can: canReadAPIKeys } = useAsyncCheckPermissions(PermissionAction.SECRETS_READ, '*')
const { data: apiKeys } = useAPIKeysQuery(
const { data: apiKeysData } = useAPIKeys(
{ projectRef, reveal: true },
{ enabled: canReadAPIKeys }
)
const { serviceKey } = getKeys(apiKeys)
const { serviceKey } = apiKeysData ?? {}
const serviceApiKey = serviceKey?.api_key ?? ''
const {
@@ -31,7 +31,7 @@ import { PublicationSelection } from './PublicationSelection'
import { ReplicationDisclaimerDialog } from './ReplicationDisclaimerDialog'
import { ValidationFailuresSection } from './ValidationFailuresSection'
import { CreateAnalyticsBucketSheet } from '@/components/interfaces/Storage/AnalyticsBuckets/CreateAnalyticsBucketSheet'
import { getKeys, useAPIKeysQuery } from '@/data/api-keys/api-keys-query'
import { useAPIKeys } from '@/data/api-keys/api-keys-query'
import { useProjectSettingsV2Query } from '@/data/config/project-settings-v2-query'
import {
BatchConfig,
@@ -149,11 +149,12 @@ export const DestinationForm = ({
pipelineId: existingDestination?.pipelineId,
})
const { data: apiKeys } = useAPIKeysQuery(
const { data: apiKeysData } = useAPIKeys(
{ projectRef, reveal: true },
{ enabled: canReadAPIKeys }
)
const { serviceKey } = getKeys(apiKeys)
const { serviceKey } = apiKeysData ?? {}
const catalogToken = serviceKey?.api_key ?? ''
const { data: projectSettings } = useProjectSettingsV2Query({ projectRef })
@@ -5,7 +5,7 @@ import CodeSnippet from './CodeSnippet'
import { DocSection } from './DocSection'
import Snippets from './Snippets'
import { InlineLink } from '@/components/ui/InlineLink'
import { getKeys, useAPIKeysQuery } from '@/data/api-keys/api-keys-query'
import { useAPIKeys } from '@/data/api-keys/api-keys-query'
import { useProjectSettingsV2Query } from '@/data/config/project-settings-v2-query'
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
@@ -17,10 +17,11 @@ interface AuthenticationProps {
const Authentication = ({ selectedLang, showApiKey }: AuthenticationProps) => {
const { ref: projectRef } = useParams()
const { can: canReadAPIKeys } = useAsyncCheckPermissions(PermissionAction.SECRETS_READ, '*')
const { data: apiKeys } = useAPIKeysQuery({ projectRef }, { enabled: canReadAPIKeys })
const { data: apiKeyData } = useAPIKeys({ projectRef }, { enabled: canReadAPIKeys })
const { anonKey, serviceKey } = apiKeyData ?? {}
const { data: settings } = useProjectSettingsV2Query({ projectRef })
const { anonKey, serviceKey } = getKeys(apiKeys)
const protocol = settings?.app_config?.protocol ?? 'https'
const hostEndpoint = settings?.app_config?.endpoint
const endpoint = `${protocol}://${hostEndpoint ?? ''}`
@@ -42,7 +42,7 @@ import z from 'zod'
import CommandRender from '../CommandRender'
import { INVOCATION_TABS } from './EdgeFunctionDetails.constants'
import { generateCLICommands } from './EdgeFunctionDetails.utils'
import { getKeys, useAPIKeysQuery } from '@/data/api-keys/api-keys-query'
import { useAPIKeys } from '@/data/api-keys/api-keys-query'
import { useProjectApiUrl } from '@/data/config/project-endpoint-query'
import { useEdgeFunctionQuery } from '@/data/edge-functions/edge-function-query'
import { useEdgeFunctionDeleteMutation } from '@/data/edge-functions/edge-functions-delete-mutation'
@@ -79,7 +79,8 @@ export const EdgeFunctionDetails = () => {
const canUpdateEdgeFunction = IS_PLATFORM && canUpdateEdgeFunctionPermission
const { can: canReadAPIKeys } = useAsyncCheckPermissions(PermissionAction.SECRETS_READ, '*')
const { data: apiKeys } = useAPIKeysQuery({ projectRef }, { enabled: canReadAPIKeys })
const { data: apiKeyData } = useAPIKeys({ projectRef }, { enabled: canReadAPIKeys })
const { anonKey, publishableKey } = apiKeyData ?? {}
const { data: selectedFunction } = useEdgeFunctionQuery({ projectRef, slug: functionSlug })
@@ -99,7 +100,6 @@ export const EdgeFunctionDetails = () => {
defaultValues: { name: '', verify_jwt: false },
})
const { anonKey, publishableKey } = getKeys(apiKeys)
const apiKey = publishableKey?.api_key ?? anonKey?.api_key ?? '[YOUR ANON KEY]'
const { managementCommands } = generateCLICommands({
@@ -39,7 +39,7 @@ import { HTTP_METHODS } from './EdgeFunctionDetails.constants'
import { ErrorWithStatus, ResponseData } from './EdgeFunctionDetails.types'
import { RoleImpersonationPopover } from '@/components/interfaces/RoleImpersonationSelector/RoleImpersonationPopover'
import { ShortcutTooltip } from '@/components/ui/ShortcutTooltip'
import { getKeys, useAPIKeysQuery } from '@/data/api-keys/api-keys-query'
import { useAPIKeys } from '@/data/api-keys/api-keys-query'
import { useSessionAccessTokenQuery } from '@/data/auth/session-access-token-query'
import { useProjectPostgrestConfigQuery } from '@/data/config/project-postgrest-config-query'
import { useProjectSettingsV2Query } from '@/data/config/project-settings-v2-query'
@@ -102,11 +102,11 @@ const EdgeFunctionTesterSheetContent = ({ visible, onClose }: EdgeFunctionTester
const [error, setError] = useState<string | null>(null)
const { can: canReadAPIKeys } = useAsyncCheckPermissions(PermissionAction.SECRETS_READ, '*')
const { data: apiKeys } = useAPIKeysQuery({ projectRef }, { enabled: canReadAPIKeys })
const { data: apiKeysData } = useAPIKeys({ projectRef }, { enabled: canReadAPIKeys })
const { serviceKey } = apiKeysData ?? {}
const { data: config } = useProjectPostgrestConfigQuery({ projectRef })
const { data: settings } = useProjectSettingsV2Query({ projectRef })
const { data: accessToken } = useSessionAccessTokenQuery({ enabled: IS_PLATFORM })
const { serviceKey } = getKeys(apiKeys)
const track = useTrack()
const { mutate: testEdgeFunction, isPending } = useEdgeFunctionTestMutation({
@@ -9,7 +9,7 @@ import type { Commands } from './Functions.types'
import CommandRender from '@/components/interfaces/Functions/CommandRender'
import { DocsButton } from '@/components/ui/DocsButton'
import { useAccessTokensQuery } from '@/data/access-tokens/access-tokens-query'
import { getKeys, useAPIKeysQuery } from '@/data/api-keys/api-keys-query'
import { useAPIKeys } from '@/data/api-keys/api-keys-query'
import { useProjectApiUrl } from '@/data/config/project-endpoint-query'
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
import { DOCS_URL } from '@/lib/constants'
@@ -29,12 +29,12 @@ export const TerminalInstructions = forwardRef<
const { data: tokens } = useAccessTokensQuery()
const { can: canReadAPIKeys } = useAsyncCheckPermissions(PermissionAction.SECRETS_READ, '*')
const { data: apiKeys } = useAPIKeysQuery({ projectRef }, { enabled: canReadAPIKeys })
const { data: apiKeyData } = useAPIKeys({ projectRef }, { enabled: canReadAPIKeys })
const { anonKey, publishableKey } = apiKeyData ?? {}
const { data: endpoint } = useProjectApiUrl({ projectRef })
const functionsEndpoint = `${endpoint}/functions/v1`
const { anonKey, publishableKey } = getKeys(apiKeys)
const apiKey = publishableKey?.api_key ?? anonKey?.api_key ?? '[YOUR ANON KEY]'
// get the .co or .net TLD from the restUrl
@@ -11,7 +11,7 @@ import { ConnectionIcon } from '@/components/interfaces/Connect/ConnectionIcon'
import { ConnectButton } from '@/components/interfaces/ConnectButton/ConnectButton'
import { AlertError } from '@/components/ui/AlertError'
import { InlineLink } from '@/components/ui/InlineLink'
import { getKeys, useAPIKeysQuery } from '@/data/api-keys/api-keys-query'
import { useAPIKeys } from '@/data/api-keys/api-keys-query'
import { useLegacyAPIKeysStatusQuery } from '@/data/api-keys/legacy-api-keys-status-query'
import { useJwtSecretUpdatingStatusQuery } from '@/data/config/jwt-secret-updating-status-query'
import { useProjectSettingsV2Query } from '@/data/config/project-settings-v2-query'
@@ -32,12 +32,12 @@ export const APIKeys = () => {
useLegacyAPIKeysStatusQuery({ projectRef }, { enabled: canReadAPIKeys })
const {
data: apiKeys,
data: apiKeysData,
error: errorAPIKeys,
isError: isErrorAPIKeys,
isPending: isLoadingAPIKeys,
} = useAPIKeysQuery({ projectRef }, { enabled: canReadAPIKeys })
const { anonKey, serviceKey, publishableKey, secretKey } = getKeys(apiKeys)
} = useAPIKeys({ projectRef }, { enabled: canReadAPIKeys })
const { anonKey, serviceKey, publishableKey, secretKey } = apiKeysData ?? {}
const hasNewAPIKeys = !!publishableKey && !!secretKey
const isLegacyKeysEnabled = legacyAPIKeysStatusData?.enabled ?? false
@@ -6,7 +6,7 @@ import { KeyValueFieldArray } from 'ui-patterns/form/KeyValueFieldArray/KeyValue
import { CreateCronJobForm } from './CreateCronJobSheet/CreateCronJobSheet.constants'
import { buildEdgeFunctionHeaderAddActions } from '@/components/interfaces/Functions/httpHeaderAddActions'
import { getKeys, useAPIKeysQuery } from '@/data/api-keys/api-keys-query'
import { useAPIKeys } from '@/data/api-keys/api-keys-query'
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
interface HTTPHeaderFieldsSectionProps {
@@ -18,12 +18,12 @@ export const HTTPHeaderFieldsSection = ({ variant }: HTTPHeaderFieldsSectionProp
const { ref } = useParams()
const { can: canReadAPIKeys } = useAsyncCheckPermissions(PermissionAction.SECRETS_READ, '*')
const { data: apiKeys } = useAPIKeysQuery(
const { data: apiKeysData } = useAPIKeys(
{ projectRef: ref, reveal: true },
{ enabled: canReadAPIKeys }
)
const { serviceKey, secretKey } = apiKeysData ?? {}
const { serviceKey, secretKey } = getKeys(apiKeys)
const apiKey = secretKey?.api_key ?? serviceKey?.api_key ?? '[YOUR API KEY]'
const addActions =
variant === 'edge_function'
@@ -9,7 +9,7 @@ import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import ContentSnippet from '../ContentSnippet'
import { DOCS_CONTENT } from '../ProjectAPIDocs.constants'
import type { ContentProps } from './Content.types'
import { getKeys, useAPIKeysQuery } from '@/data/api-keys/api-keys-query'
import { useAPIKeys } from '@/data/api-keys/api-keys-query'
import { useProjectSettingsV2Query } from '@/data/config/project-settings-v2-query'
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
import { useTrack } from '@/lib/telemetry/track'
@@ -17,8 +17,10 @@ import { useTrack } from '@/lib/telemetry/track'
export const Introduction = ({ showKeys, language, apikey, endpoint }: ContentProps) => {
const { ref } = useParams()
const { can: canReadAPIKeys } = useAsyncCheckPermissions(PermissionAction.SECRETS_READ, '*')
const { data: apiKeys } = useAPIKeysQuery({ projectRef: ref }, { enabled: canReadAPIKeys })
const { data: apiKeysData } = useAPIKeys({ projectRef: ref }, { enabled: canReadAPIKeys })
useProjectSettingsV2Query({ projectRef: ref })
const { anonKey, serviceKey } = apiKeysData ?? {}
const track = useTrack()
const [copied, setCopied] = useState<'anon' | 'service'>()
@@ -27,7 +29,6 @@ export const Introduction = ({ showKeys, language, apikey, endpoint }: ContentPr
if (copied !== undefined) setTimeout(() => setCopied(undefined), 2000)
}, [copied])
const { anonKey, serviceKey } = getKeys(apiKeys)
const anonApiKey = anonKey?.api_key
const serviceApiKey = serviceKey?.api_key ?? 'SUPABASE_CLIENT_SERVICE_KEY'
@@ -17,7 +17,7 @@ import { UserManagement } from './Content/UserManagement'
import { FirstLevelNav } from './FirstLevelNav'
import LanguageSelector from './LanguageSelector'
import { SecondLevelNav } from './SecondLevelNav'
import { getKeys, useAPIKeysQuery } from '@/data/api-keys/api-keys-query'
import { useAPIKeys } from '@/data/api-keys/api-keys-query'
import { useProjectApiUrl } from '@/data/config/project-endpoint-query'
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
import { useAppStateSnapshot } from '@/state/app-state'
@@ -45,17 +45,17 @@ export const ProjectAPIDocs = () => {
const language = snap.docsLanguage
const { can: canReadAPIKeys } = useAsyncCheckPermissions(PermissionAction.SECRETS_READ, '*')
const { data: apiKeys } = useAPIKeysQuery(
const { data: apiKeysData } = useAPIKeys(
{ projectRef: ref },
{ enabled: snap.showProjectApiDocs && canReadAPIKeys }
)
const { anonKey } = apiKeysData ?? {}
const { data: endpoint } = useProjectApiUrl(
{ projectRef: ref },
{ enabled: snap.showProjectApiDocs }
)
const { anonKey } = getKeys(apiKeys)
const apikey = showKeys
? (anonKey?.api_key ?? 'SUPABASE_CLIENT_ANON_KEY')
: 'SUPABASE_CLIENT_ANON_KEY'
@@ -15,7 +15,7 @@ import {
import { ShimmeringLoader } from 'ui-patterns'
import { getConnectionStrings } from '@/components/interfaces/Connect/DatabaseSettings.utils'
import { getKeys, useAPIKeysQuery } from '@/data/api-keys/api-keys-query'
import { useAPIKeys } from '@/data/api-keys/api-keys-query'
import { useProjectApiUrl } from '@/data/config/project-endpoint-query'
import { useReadReplicasQuery } from '@/data/read-replicas/replicas-query'
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
@@ -47,11 +47,11 @@ export const ProjectConnectionPopover = ({ projectRef }: ProjectConnectionPopove
const { data: projectUrl, isPending: isLoadingApiUrl } = useProjectApiUrl({ projectRef })
const { data: apiKeys, isLoading: isLoadingKeys } = useAPIKeysQuery(
const { data, isLoading: isLoadingKeys } = useAPIKeys(
{ projectRef },
{ enabled: open && canReadAPIKeys }
)
const { publishableKey } = canReadAPIKeys ? getKeys(apiKeys) : { publishableKey: null }
const { publishableKey } = data ?? {}
const { data: databases, isLoading: isLoadingDatabases } = useReadReplicasQuery(
{ projectRef },
@@ -4,7 +4,7 @@ import { toast } from 'sonner'
import { RealtimeConfig } from './useRealtimeMessages'
import { RoleImpersonationPopover } from '@/components/interfaces/RoleImpersonationSelector/RoleImpersonationPopover'
import { getKeys, useAPIKeysQuery } from '@/data/api-keys/api-keys-query'
import { useAPIKeys } from '@/data/api-keys/api-keys-query'
import { getTemporaryAPIKey } from '@/data/api-keys/temp-api-keys-query'
import { useProjectPostgrestConfigQuery } from '@/data/config/project-postgrest-config-query'
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
@@ -22,15 +22,13 @@ export const RealtimeTokensPopover = ({ config, onChangeConfig }: RealtimeTokens
const snap = useRoleImpersonationStateSnapshot()
const { can: canReadAPIKeys } = useAsyncCheckPermissions(PermissionAction.SECRETS_READ, '*')
const { data: apiKeys } = useAPIKeysQuery(
const { data: apiKeysData } = useAPIKeys(
{
projectRef: config.projectRef,
reveal: true,
},
{ enabled: canReadAPIKeys }
)
const { anonKey, publishableKey } = getKeys(apiKeys)
const { data: postgrestConfig } = useProjectPostgrestConfigQuery(
{ projectRef: config.projectRef },
{ enabled: IS_PLATFORM }
@@ -55,6 +53,7 @@ export const RealtimeTokensPopover = ({ config, onChangeConfig }: RealtimeTokens
}, [snap.role])
useEffect(() => {
const { anonKey, publishableKey } = apiKeysData ?? {}
const triggerUpdateTokenBearer = async () => {
let token: string | undefined
let bearer: string | null = null
@@ -84,7 +83,7 @@ export const RealtimeTokensPopover = ({ config, onChangeConfig }: RealtimeTokens
triggerUpdateTokenBearer()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [snap.role, anonKey])
}, [snap.role, apiKeysData])
return <RoleImpersonationPopover align="start" variant="connected-on-both" />
}
@@ -28,7 +28,7 @@ import { CodeBlock } from 'ui-patterns/CodeBlock'
import { useS3VectorsWrapperExtension } from '../useS3VectorsWrapper'
import { useS3VectorsWrapperInstance } from '../useS3VectorsWrapperInstance'
import { DocsButton } from '@/components/ui/DocsButton'
import { getKeys, useAPIKeysQuery } from '@/data/api-keys/api-keys-query'
import { useAPIKeys } from '@/data/api-keys/api-keys-query'
import { VectorBucketIndex } from '@/data/storage/vector-buckets-indexes-query'
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
import { DOCS_URL } from '@/lib/constants'
@@ -143,8 +143,8 @@ function VectorBucketIndexExamples({
PermissionAction.READ,
'service_api_keys'
)
const { data: apiKeys } = useAPIKeysQuery({ projectRef }, { enabled: canReadAPIKeys })
const { secretKey } = canReadAPIKeys ? getKeys(apiKeys) : { secretKey: null }
const { data: apiKeysData } = useAPIKeys({ projectRef }, { enabled: canReadAPIKeys })
const { secretKey } = apiKeysData ?? {}
const { data: wrapperInstance } = useS3VectorsWrapperInstance({ bucketId })
const foreignTable = wrapperInstance?.tables?.find((x) => x.name === indexName)
@@ -177,7 +177,7 @@ values
const jsCode = `import { createClient } from '@supabase/supabase-js'
// Adding vector data requires a secret or service role key
// Adding vector data requires a secret or service role key
// This code SHOULD NOT be run on the client side as you will be vulnerable to a data leak
const client = createClient(
process.env.SUPABASE_URL,
@@ -1,4 +1,5 @@
import { useQuery } from '@tanstack/react-query'
import { useCallback } from 'react'
import { apiKeysKeys } from './keys'
import { get, handleError } from '@/data/fetchers'
@@ -78,7 +79,18 @@ export const useAPIKeysQuery = <TData = APIKeysData>(
})
}
const EmptyKey = {
anonKey: undefined,
serviceKey: undefined,
publishableKey: undefined,
secretKey: undefined,
allSecretKeys: [],
}
export const getKeys = (apiKeys: APIKey[] = []) => {
if (!Array.isArray(apiKeys)) {
return EmptyKey
}
const anonKey = apiKeys.find((x) => x.name === 'anon')
const serviceKey = apiKeys.find((x) => x.name === 'service_role')
@@ -90,3 +102,21 @@ export const getKeys = (apiKeys: APIKey[] = []) => {
return { anonKey, serviceKey, publishableKey, secretKey, allSecretKeys }
}
const PlaceholderData: APIKeysData = []
export const useAPIKeys = (
variables: APIKeysVariables,
options: UseCustomQueryOptions<APIKeysData, ResponseError, APIKeysData> = {}
) => {
const select = useCallback(
(data: APIKeysData) => getKeys(options.enabled ? data : []),
[options.enabled]
)
return useAPIKeysQuery(variables, {
...options,
placeholderData: PlaceholderData,
select,
})
}
@@ -10,7 +10,7 @@ import {
getCatalogURI,
getConnectionURL,
} from '@/components/interfaces/Storage/StorageSettings/StorageSettings.utils'
import { getKeys, useAPIKeysQuery } from '@/data/api-keys/api-keys-query'
import { useAPIKeys } from '@/data/api-keys/api-keys-query'
import { useProjectSettingsV2Query } from '@/data/config/project-settings-v2-query'
import { FDWCreateVariables, useFDWCreateMutation } from '@/data/fdw/fdw-create-mutation'
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
@@ -20,12 +20,11 @@ export const useIcebergWrapperCreateMutation = () => {
const { data: project } = useSelectedProjectQuery()
const { can: canReadAPIKeys } = useAsyncCheckPermissions(PermissionAction.SECRETS_READ, '*')
const { data: apiKeys } = useAPIKeysQuery(
const { data: apiKeysData } = useAPIKeys(
{ projectRef: project?.ref, reveal: true },
{ enabled: canReadAPIKeys }
)
const { secretKey, serviceKey } = getKeys(apiKeys)
const { secretKey, serviceKey } = apiKeysData ?? {}
const { data: settings } = useProjectSettingsV2Query({ projectRef: project?.ref })
const protocol = settings?.app_config?.protocol ?? 'https'
const endpoint = settings?.app_config?.storage_endpoint || settings?.app_config?.endpoint