From eced4c28519efbe2db9bdf714c73ecf1a03f1a4a Mon Sep 17 00:00:00 2001 From: phamhieu Date: Tue, 29 Sep 2026 11:29:43 +0700 Subject: [PATCH] refactor: use useAsyncCheckPermissionsV2 in api keys --- .../interfaces/APIKeys/APIKeyDeleteDialog.tsx | 8 +++----- .../components/interfaces/APIKeys/ApiKeyPill.test.tsx | 11 ++++++----- .../components/interfaces/APIKeys/ApiKeyPill.tsx | 8 +++----- .../interfaces/APIKeys/PublishableAPIKeys.test.tsx | 11 ++++++----- .../interfaces/APIKeys/PublishableAPIKeys.tsx | 8 +++----- .../components/interfaces/APIKeys/SecretAPIKeys.tsx | 8 +++----- .../interfaces/App/CommandMenu/ApiKeys.test.tsx | 11 ++++++----- .../components/interfaces/App/CommandMenu/ApiKeys.tsx | 5 ++--- 8 files changed, 32 insertions(+), 38 deletions(-) diff --git a/apps/studio/components/interfaces/APIKeys/APIKeyDeleteDialog.tsx b/apps/studio/components/interfaces/APIKeys/APIKeyDeleteDialog.tsx index 0c2bab785ba..66274e092e0 100644 --- a/apps/studio/components/interfaces/APIKeys/APIKeyDeleteDialog.tsx +++ b/apps/studio/components/interfaces/APIKeys/APIKeyDeleteDialog.tsx @@ -1,9 +1,8 @@ -import { PermissionAction } from '@supabase/shared-types/out/constants' import { Trash2 } from 'lucide-react' import { DropdownMenuItemTooltip } from '@/components/ui/DropdownMenuItemTooltip' import type { APIKeysData } from '@/data/api-keys/api-keys-query' -import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions' +import { FGA_PERMISSIONS, useAsyncCheckPermissionsV2 } from '@/hooks/misc/useCheckPermissionsV2' interface APIKeyDeleteDialogProps { apiKey: Extract @@ -11,9 +10,8 @@ interface APIKeyDeleteDialogProps { } export const APIKeyDeleteDialog = ({ apiKey, setKeyToDelete }: APIKeyDeleteDialogProps) => { - const { can: canDeleteAPIKeys } = useAsyncCheckPermissions( - PermissionAction.TENANT_SQL_ADMIN_WRITE, - '*' + const { can: canDeleteAPIKeys } = useAsyncCheckPermissionsV2( + FGA_PERMISSIONS.PROJECT.API_GATEWAY_KEYS_WRITE ) return ( diff --git a/apps/studio/components/interfaces/APIKeys/ApiKeyPill.test.tsx b/apps/studio/components/interfaces/APIKeys/ApiKeyPill.test.tsx index 8b5ba531696..3180df87e0e 100644 --- a/apps/studio/components/interfaces/APIKeys/ApiKeyPill.test.tsx +++ b/apps/studio/components/interfaces/APIKeys/ApiKeyPill.test.tsx @@ -12,11 +12,12 @@ type ApiKeyResponse = components['schemas']['ApiKeyResponse_Output'] // Permissions are non-network global state — mock the hook (the skill's allowed // exception), matching the neighboring PublishableAPIKeys.test.tsx. -const { mockUseAsyncCheckPermissions } = vi.hoisted(() => ({ - mockUseAsyncCheckPermissions: vi.fn(), +const { mockUseAsyncCheckPermissionsV2 } = vi.hoisted(() => ({ + mockUseAsyncCheckPermissionsV2: vi.fn(), })) -vi.mock('@/hooks/misc/useCheckPermissions', () => ({ - useAsyncCheckPermissions: mockUseAsyncCheckPermissions, +vi.mock('@/hooks/misc/useCheckPermissionsV2', async (importOriginal) => ({ + ...(await importOriginal()), + useAsyncCheckPermissionsV2: mockUseAsyncCheckPermissionsV2, })) // CopyButton writes via copyToClipboard from 'ui'. Stub just that export so we can @@ -60,7 +61,7 @@ function mockRevealById() { describe('ApiKeyPill', () => { beforeEach(() => { - mockUseAsyncCheckPermissions.mockReturnValue({ can: true, isLoading: false }) + mockUseAsyncCheckPermissionsV2.mockReturnValue({ can: true, isLoading: false, isSuccess: true }) mockCopyToClipboard.mockClear() }) diff --git a/apps/studio/components/interfaces/APIKeys/ApiKeyPill.tsx b/apps/studio/components/interfaces/APIKeys/ApiKeyPill.tsx index f4b6f6e6c43..a15b41286dd 100644 --- a/apps/studio/components/interfaces/APIKeys/ApiKeyPill.tsx +++ b/apps/studio/components/interfaces/APIKeys/ApiKeyPill.tsx @@ -1,4 +1,3 @@ -import { PermissionAction } from '@supabase/shared-types/out/constants' import { useParams } from 'common' import { Eye, EyeOff } from 'lucide-react' import { useEffect, useState } from 'react' @@ -8,7 +7,7 @@ import { Button, cn, InputVariants, Tooltip, TooltipContent, TooltipTrigger } fr import { useRevealedSecret } from './useRevealedSecret' import CopyButton from '@/components/ui/CopyButton' import { APIKeysData } from '@/data/api-keys/api-keys-query' -import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions' +import { FGA_PERMISSIONS, useAsyncCheckPermissionsV2 } from '@/hooks/misc/useCheckPermissionsV2' export function ApiKeyPill({ apiKey, @@ -20,9 +19,8 @@ export function ApiKeyPill({ const isSecret = apiKey.type === 'secret' - const { can: canManageSecretKeys, isLoading: isLoadingPermission } = useAsyncCheckPermissions( - PermissionAction.READ, - 'service_api_keys' + const { can: canManageSecretKeys, isLoading: isLoadingPermission } = useAsyncCheckPermissionsV2( + [FGA_PERMISSIONS.PROJECT.API_GATEWAY_KEYS_SECRET_READ] ) const { diff --git a/apps/studio/components/interfaces/APIKeys/PublishableAPIKeys.test.tsx b/apps/studio/components/interfaces/APIKeys/PublishableAPIKeys.test.tsx index 0ad7a85a061..0420f4ee9ab 100644 --- a/apps/studio/components/interfaces/APIKeys/PublishableAPIKeys.test.tsx +++ b/apps/studio/components/interfaces/APIKeys/PublishableAPIKeys.test.tsx @@ -7,12 +7,12 @@ import { customRender as render } from '@/tests/lib/custom-render' const { mockIsPlatform, mockUseAPIKeysQuery, - mockUseAsyncCheckPermissions, + mockUseAsyncCheckPermissionsV2, mockUseAPIKeyDeleteMutation, } = vi.hoisted(() => ({ mockIsPlatform: { value: true }, mockUseAPIKeysQuery: vi.fn(), - mockUseAsyncCheckPermissions: vi.fn(), + mockUseAsyncCheckPermissionsV2: vi.fn(), mockUseAPIKeyDeleteMutation: vi.fn(), })) @@ -35,8 +35,9 @@ vi.mock('@/data/api-keys/api-key-delete-mutation', () => ({ useAPIKeyDeleteMutation: mockUseAPIKeyDeleteMutation, })) -vi.mock('@/hooks/misc/useCheckPermissions', () => ({ - useAsyncCheckPermissions: mockUseAsyncCheckPermissions, +vi.mock('@/hooks/misc/useCheckPermissionsV2', async (importOriginal) => ({ + ...(await importOriginal()), + useAsyncCheckPermissionsV2: mockUseAsyncCheckPermissionsV2, })) vi.mock('./CreatePublishableAPIKeyDialog', () => ({ @@ -68,7 +69,7 @@ const secretKey = { describe('PublishableAPIKeys', () => { beforeEach(() => { mockIsPlatform.value = true - mockUseAsyncCheckPermissions.mockReturnValue({ can: true, isLoading: false }) + mockUseAsyncCheckPermissionsV2.mockReturnValue({ can: true, isLoading: false, isSuccess: true }) mockUseAPIKeyDeleteMutation.mockReturnValue({ mutate: vi.fn(), isPending: false, diff --git a/apps/studio/components/interfaces/APIKeys/PublishableAPIKeys.tsx b/apps/studio/components/interfaces/APIKeys/PublishableAPIKeys.tsx index 0c4b3ecfb52..1c2f0b02ad3 100644 --- a/apps/studio/components/interfaces/APIKeys/PublishableAPIKeys.tsx +++ b/apps/studio/components/interfaces/APIKeys/PublishableAPIKeys.tsx @@ -1,4 +1,3 @@ -import { PermissionAction } from '@supabase/shared-types/out/constants' import { IS_PLATFORM, useParams } from 'common' import { parseAsString, useQueryState } from 'nuqs' import { useEffect, useMemo } from 'react' @@ -23,13 +22,12 @@ import { FormHeader } from '@/components/ui/Forms/FormHeader' import { NoPermission } from '@/components/ui/NoPermission' import { useAPIKeyDeleteMutation } from '@/data/api-keys/api-key-delete-mutation' import { APIKeysData, useAPIKeysQuery } from '@/data/api-keys/api-keys-query' -import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions' +import { FGA_PERMISSIONS, useAsyncCheckPermissionsV2 } from '@/hooks/misc/useCheckPermissionsV2' export const PublishableAPIKeys = () => { const { ref: projectRef } = useParams() - const { can: canReadAPIKeys, isLoading: isLoadingPermissions } = useAsyncCheckPermissions( - PermissionAction.SECRETS_READ, - '*' + const { can: canReadAPIKeys, isLoading: isLoadingPermissions } = useAsyncCheckPermissionsV2( + FGA_PERMISSIONS.PROJECT.API_GATEWAY_KEYS_READ ) const { diff --git a/apps/studio/components/interfaces/APIKeys/SecretAPIKeys.tsx b/apps/studio/components/interfaces/APIKeys/SecretAPIKeys.tsx index a863818bf62..9a8924adfae 100644 --- a/apps/studio/components/interfaces/APIKeys/SecretAPIKeys.tsx +++ b/apps/studio/components/interfaces/APIKeys/SecretAPIKeys.tsx @@ -1,4 +1,3 @@ -import { PermissionAction } from '@supabase/shared-types/out/constants' import { IS_PLATFORM, useParams } from 'common' import { parseAsString, useQueryState } from 'nuqs' import { useEffect, useMemo } from 'react' @@ -14,13 +13,12 @@ import { NoPermission } from '@/components/ui/NoPermission' import { useAPIKeyDeleteMutation } from '@/data/api-keys/api-key-delete-mutation' import type { APIKeysData } from '@/data/api-keys/api-keys-query' import { useAPIKeysQuery } from '@/data/api-keys/api-keys-query' -import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions' +import { FGA_PERMISSIONS, useAsyncCheckPermissionsV2 } from '@/hooks/misc/useCheckPermissionsV2' export const SecretAPIKeys = () => { const { ref: projectRef } = useParams() - const { can: canReadAPIKeys, isLoading: isLoadingPermissions } = useAsyncCheckPermissions( - PermissionAction.SECRETS_READ, - '*' + const { can: canReadAPIKeys, isLoading: isLoadingPermissions } = useAsyncCheckPermissionsV2( + FGA_PERMISSIONS.PROJECT.API_GATEWAY_KEYS_READ ) const { diff --git a/apps/studio/components/interfaces/App/CommandMenu/ApiKeys.test.tsx b/apps/studio/components/interfaces/App/CommandMenu/ApiKeys.test.tsx index 24ead7407b6..5532be32f46 100644 --- a/apps/studio/components/interfaces/App/CommandMenu/ApiKeys.test.tsx +++ b/apps/studio/components/interfaces/App/CommandMenu/ApiKeys.test.tsx @@ -12,15 +12,16 @@ import { addAPIMock } from '@/tests/lib/msw' type ApiKeyResponse = components['schemas']['ApiKeyResponse_Output'] -const { mockUseAsyncCheckPermissions, mockUseHighAvailability, mockUseSelectedProjectQuery } = +const { mockUseAsyncCheckPermissionsV2, mockUseHighAvailability, mockUseSelectedProjectQuery } = vi.hoisted(() => ({ - mockUseAsyncCheckPermissions: vi.fn(), + mockUseAsyncCheckPermissionsV2: vi.fn(), mockUseHighAvailability: vi.fn(), mockUseSelectedProjectQuery: vi.fn(), })) -vi.mock('@/hooks/misc/useCheckPermissions', () => ({ - useAsyncCheckPermissions: mockUseAsyncCheckPermissions, +vi.mock('@/hooks/misc/useCheckPermissionsV2', async (importOriginal) => ({ + ...(await importOriginal()), + useAsyncCheckPermissionsV2: mockUseAsyncCheckPermissionsV2, })) vi.mock('@/hooks/misc/useHighAvailability', () => ({ @@ -80,7 +81,7 @@ async function renderCommandPage() { describe('useApiKeysCommands', () => { beforeEach(() => { - mockUseAsyncCheckPermissions.mockReturnValue({ can: true }) + mockUseAsyncCheckPermissionsV2.mockReturnValue({ can: true, isLoading: false, isSuccess: true }) mockUseSelectedProjectQuery.mockReturnValue({ data: { id: 1, ref: 'default', name: 'default' }, }) diff --git a/apps/studio/components/interfaces/App/CommandMenu/ApiKeys.tsx b/apps/studio/components/interfaces/App/CommandMenu/ApiKeys.tsx index 7788073e507..f47f58b3063 100644 --- a/apps/studio/components/interfaces/App/CommandMenu/ApiKeys.tsx +++ b/apps/studio/components/interfaces/App/CommandMenu/ApiKeys.tsx @@ -1,4 +1,3 @@ -import { PermissionAction } from '@supabase/shared-types/out/constants' import { Key } from 'lucide-react' import { useMemo } from 'react' import { toast } from 'sonner' @@ -16,9 +15,9 @@ import { import { COMMAND_MENU_SECTIONS } from './CommandMenu.utils' import { orderCommandSectionsByPriority } from './ordering' import { useAPIKeys } from '@/data/api-keys/api-keys-query' -import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions' import { useHighAvailability } from '@/hooks/misc/useHighAvailability' import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject' +import { FGA_PERMISSIONS, useAsyncCheckPermissionsV2 } from '@/hooks/misc/useCheckPermissionsV2' const API_KEYS_PAGE_NAME = 'API Keys' @@ -30,7 +29,7 @@ export function useApiKeysCommands() { const { data: project } = useSelectedProjectQuery() const ref = project?.ref || '_' - const { can: canReadAPIKeys } = useAsyncCheckPermissions(PermissionAction.SECRETS_READ, '*') + const { can: canReadAPIKeys } = useAsyncCheckPermissionsV2(FGA_PERMISSIONS.PROJECT.API_GATEWAY_KEYS_READ) const { isHighAvailability } = useHighAvailability() const { data: apiKeysData } = useAPIKeys(