Files
supabase/apps/studio/data/oauth/authorized-app-revoke-mutation.ts
T
Matt Linkous 171ca026b5 feat(studio): Add integration settings page with connected resources (#46961)
Adds integrations settings page to each oauth integration to show
associated resources (e.g. API keys, config, oauth apps, etc)

<img width="1150" height="892" alt="Screenshot 2026-06-16 at 2 44 31 PM"
src="https://github.com/user-attachments/assets/035cc602-886d-43bc-a5a7-e14f76dd37c3"
/>



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

## Summary

* **New Features**
* Added a Marketplace “Settings” tab with a grouped **Connected
resources** view (OAuth apps, API keys, Edge Function secrets, SMTP),
including loading/empty/missing-resource states and per-kind removal
actions.
* Added a resource-group section UI plus integration-aware grouping/copy
customization and missing-kind zero-states.
* **Bug Fixes**
* Improved installed-state detection for Grafana and Doppler by
broadening conditions.
* Added an orphaned-resources warning when expected OAuth apps are
missing.
* **Refactor**
* Unified connected-resource removal into a single flow with
OAuth-specific revoke handling.
* **Tests**
* Added comprehensive UI and utility coverage for grouping, states, and
destructive removal behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-22 20:51:14 +00:00

54 lines
1.7 KiB
TypeScript

import { useMutation, useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
import { oauthAppKeys } from './keys'
import { handleError, post } from '@/data/fetchers'
import type { ResponseError, UseCustomMutationOptions } from '@/types'
export type AuthorizedAppRevokeVariables = {
id: string
orgSlug: string
}
export async function revokeAuthorizedApp({ id, orgSlug: slug }: AuthorizedAppRevokeVariables) {
if (!id) throw new Error('App ID is required')
if (!slug) throw new Error('Organization slug is required')
const { data, error } = await post('/platform/organizations/{slug}/oauth/apps/{id}/revoke', {
params: { path: { slug, id } },
})
if (error) handleError(error)
return data
}
type AuthorizedAppRevokeData = Awaited<ReturnType<typeof revokeAuthorizedApp>>
export const useAuthorizedAppRevokeMutation = ({
onSuccess,
onError,
...options
}: Omit<
UseCustomMutationOptions<AuthorizedAppRevokeData, ResponseError, AuthorizedAppRevokeVariables>,
'mutationFn'
> = {}) => {
const queryClient = useQueryClient()
return useMutation<AuthorizedAppRevokeData, ResponseError, AuthorizedAppRevokeVariables>({
mutationFn: (vars) => revokeAuthorizedApp(vars),
async onSuccess(data, variables, context) {
const { orgSlug: slug } = variables
await queryClient.invalidateQueries({ queryKey: oauthAppKeys.authorizedApps(slug) })
await onSuccess?.(data, variables, context)
},
async onError(data, variables, context) {
if (onError === undefined) {
toast.error(`Failed to revoke application: ${data.message}`)
} else {
onError(data, variables, context)
}
},
...options,
})
}