| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131 |
- /**
- * TanStack Query hooks for the /v1/tenants/{id}/sources/* endpoints.
- *
- * The hooks are feature-scoped: callers (list, create-dialog,
- * detail-page) pull these and don't talk to fetchWithAuth
- * directly. That way the cache is shared across views and the
- * query keys are predictable.
- *
- * Cache key strategy: ['sources', 'list', tenantId, params] for
- * the list, ['sources', 'detail', tenantId, sourceId] for the
- * detail. tenantId is part of the key (not just the URL) so the
- * list cache for tenant A doesn't leak when the user navigates
- * to tenant B.
- */
- import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
- import { ApiError, apiGet, apiSend } from '@/lib/api';
- import type {
- CreateOrRotateResponse,
- CreateSourceInput,
- ListSourcesResponse,
- Source,
- UpdateSourceInput,
- } from './types';
- export interface ListSourcesParams {
- q?: string;
- type?: string;
- status?: string;
- limit?: number;
- offset?: number;
- }
- const KEYS = {
- list: (tenantId: string | undefined, params: ListSourcesParams) =>
- ['sources', 'list', tenantId, params] as const,
- detail: (tenantId: string | undefined, sourceId: string | undefined) =>
- ['sources', 'detail', tenantId, sourceId] as const,
- };
- function buildListQuery(tenantId: string, params: ListSourcesParams): string {
- const u = new URLSearchParams();
- if (params.q) u.set('q', params.q);
- if (params.type) u.set('type', params.type);
- if (params.status) u.set('status', params.status);
- if (params.limit) u.set('limit', String(params.limit));
- if (params.offset) u.set('offset', String(params.offset));
- const s = u.toString();
- return s
- ? `/v1/tenants/${tenantId}/sources?${s}`
- : `/v1/tenants/${tenantId}/sources`;
- }
- export function useSourcesList(tenantId: string | undefined, params: ListSourcesParams) {
- return useQuery({
- queryKey: KEYS.list(tenantId, params),
- queryFn: () => apiGet<ListSourcesResponse>(buildListQuery(tenantId as string, params)),
- enabled: Boolean(tenantId),
- staleTime: 15_000,
- });
- }
- export function useSource(tenantId: string | undefined, sourceId: string | undefined) {
- return useQuery({
- queryKey: KEYS.detail(tenantId, sourceId),
- queryFn: () => apiGet<Source>(`/v1/tenants/${tenantId}/sources/${sourceId}`),
- enabled: Boolean(tenantId) && Boolean(sourceId),
- });
- }
- export function useCreateSource(tenantId: string) {
- const qc = useQueryClient();
- return useMutation({
- mutationFn: (input: CreateSourceInput) =>
- apiSend<CreateOrRotateResponse>('POST', `/v1/tenants/${tenantId}/sources`, input),
- onSuccess: () => {
- void qc.invalidateQueries({ queryKey: ['sources', 'list', tenantId] });
- },
- });
- }
- export function useUpdateSource(tenantId: string, sourceId: string) {
- const qc = useQueryClient();
- return useMutation({
- mutationFn: (input: UpdateSourceInput) =>
- apiSend<Source>('PATCH', `/v1/tenants/${tenantId}/sources/${sourceId}`, input),
- onSuccess: (source) => {
- qc.setQueryData(KEYS.detail(tenantId, sourceId), source);
- void qc.invalidateQueries({ queryKey: ['sources', 'list', tenantId] });
- },
- });
- }
- export function useSetSourceStatus(tenantId: string, sourceId: string) {
- const qc = useQueryClient();
- return useMutation({
- mutationFn: (status: 'active' | 'suspended') =>
- apiSend<Source>('POST', `/v1/tenants/${tenantId}/sources/${sourceId}/status`, {
- status,
- }),
- onSuccess: (source) => {
- qc.setQueryData(KEYS.detail(tenantId, sourceId), source);
- void qc.invalidateQueries({ queryKey: ['sources', 'list', tenantId] });
- },
- });
- }
- export function useRotateSourceSecrets(tenantId: string, sourceId: string) {
- const qc = useQueryClient();
- return useMutation({
- mutationFn: () =>
- apiSend<CreateOrRotateResponse>(
- 'POST',
- `/v1/tenants/${tenantId}/sources/${sourceId}/rotate-secrets`,
- {},
- ),
- onSuccess: (resp) => {
- qc.setQueryData(KEYS.detail(tenantId, sourceId), resp.source);
- void qc.invalidateQueries({ queryKey: ['sources', 'list', tenantId] });
- },
- });
- }
- export function getErrorMessage(err: unknown): string {
- if (err instanceof ApiError) {
- const body = err.body as { error?: string; message?: string } | null;
- return body?.message ?? body?.error ?? err.message;
- }
- if (err instanceof Error) return err.message;
- return 'Unknown error';
- }
|