Files
cameleer-server/ui/src/api/queries/commands.ts
hsiegeln 2b1d49c032 feat: add TapDefinition, extend ApplicationConfig, and add API hooks
- Add TapDefinition interface for tap configuration
- Extend ApplicationConfig with taps, tapVersion, routeRecording, compressSuccess
- Add useTestExpression mutation hook (manual fetch to new endpoint)
- Add useReplayExchange mutation hook (uses api client, targets single agent)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 18:29:52 +01:00

164 lines
5.1 KiB
TypeScript

import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { api } from '../client'
import { useAuthStore } from '../../auth/auth-store'
// ── Application Config ────────────────────────────────────────────────────
export interface TapDefinition {
tapId: string
processorId: string
target: 'INPUT' | 'OUTPUT' | 'BOTH'
expression: string
language: string
attributeName: string
attributeType: 'BUSINESS_OBJECT' | 'CORRELATION' | 'EVENT' | 'CUSTOM'
enabled: boolean
version: number
}
export interface ApplicationConfig {
application: string
version: number
updatedAt?: string
engineLevel?: string
payloadCaptureMode?: string
logForwardingLevel?: string
metricsEnabled: boolean
samplingRate: number
tracedProcessors: Record<string, string>
taps: TapDefinition[]
tapVersion: number
routeRecording: Record<string, boolean>
compressSuccess: boolean
}
/** Authenticated fetch using the JWT from auth store */
function authFetch(url: string, init?: RequestInit): Promise<Response> {
const token = useAuthStore.getState().accessToken
const headers = new Headers(init?.headers)
if (token) headers.set('Authorization', `Bearer ${token}`)
headers.set('X-Cameleer-Protocol-Version', '1')
return fetch(url, { ...init, headers })
}
export function useAllApplicationConfigs() {
return useQuery({
queryKey: ['applicationConfig', 'all'],
queryFn: async () => {
const res = await authFetch('/api/v1/config')
if (!res.ok) throw new Error('Failed to fetch configs')
return res.json() as Promise<ApplicationConfig[]>
},
})
}
export function useApplicationConfig(application: string | undefined) {
return useQuery({
queryKey: ['applicationConfig', application],
queryFn: async () => {
const res = await authFetch(`/api/v1/config/${application}`)
if (!res.ok) throw new Error('Failed to fetch config')
return res.json() as Promise<ApplicationConfig>
},
enabled: !!application,
})
}
export function useUpdateApplicationConfig() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: async (config: ApplicationConfig) => {
const res = await authFetch(`/api/v1/config/${config.application}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(config),
})
if (!res.ok) throw new Error('Failed to update config')
return res.json() as Promise<ApplicationConfig>
},
onSuccess: (saved) => {
queryClient.setQueryData(['applicationConfig', saved.application], saved)
queryClient.invalidateQueries({ queryKey: ['applicationConfig', 'all'] })
},
})
}
// ── Generic Group Command (kept for non-config commands) ──────────────────
interface SendGroupCommandParams {
group: string
type: string
payload: Record<string, unknown>
}
export function useSendGroupCommand() {
return useMutation({
mutationFn: async ({ group, type, payload }: SendGroupCommandParams) => {
const { data, error } = await api.POST('/agents/groups/{group}/commands', {
params: { path: { group } },
body: { type, payload } as any,
})
if (error) throw new Error('Failed to send command')
return data!
},
})
}
// ── Test Expression ───────────────────────────────────────────────────────
export function useTestExpression() {
return useMutation({
mutationFn: async ({
application,
expression,
language,
body,
target,
}: {
application: string
expression: string
language: string
body: string
target: string
}) => {
const res = await authFetch(
`/api/v1/config/${encodeURIComponent(application)}/test-expression`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ expression, language, body, target }),
},
)
if (!res.ok) {
if (res.status === 404) throw new Error('No live agent available')
if (res.status === 504) throw new Error('Expression test timed out')
throw new Error('Failed to test expression')
}
return res.json() as Promise<{ result?: string; error?: string }>
},
})
}
// ── Replay Exchange ───────────────────────────────────────────────────────
export function useReplayExchange() {
return useMutation({
mutationFn: async ({
agentId,
headers,
body,
}: {
agentId: string
headers: Record<string, string>
body: string
}) => {
const { data, error } = await api.POST('/agents/{id}/commands', {
params: { path: { id: agentId } },
body: { type: 'replay', payload: { headers, body } } as any,
})
if (error) throw new Error('Failed to send replay command')
return data!
},
})
}