Files
zeekay 40d120dbf7 refactor(api): say /v1/ in the Python callers
Every first-party Python caller and test now names the mount the API actually
answers on. Mechanical: URL literals only, and only where the /api/ was ours --
a preceding word character means it was a module path like insights/api/ or a
third-party host like slack.com/api/, and those are untouched.

Left saying /api/, each for a reason rather than for safety:
- products/warehouse_sources/.../sources/** is a catalogue of vendor endpoints
- services/llm-gateway deploys separately and is under a code freeze, so moving
  its two calls would break it during the skew
- the mcp_store and Slack/Teams redirect_uris are registered with third parties
  and must byte-match what was registered
- the Wizard and Raycast client-metadata paths are OAuth client_ids
- /api/surveys and /api/feature_flag/local_evaluation are SDK aliases the
  ingress routes to the Rust fleets, held by released SDK versions

Test URLconfs moved with the requests that call them, and the Datastore
user-selection fixtures now carry the leading slash that request.path has.
2026-08-08 18:38:44 -07:00

160 lines
5.0 KiB
TypeScript

import { MakeLogicType, actions, connect, kea, listeners, path, reducers } from 'kea'
import { loaders } from 'kea-loaders'
import { toast } from '@hanzo/elements'
import api from 'lib/api'
import { teamLogic } from 'scenes/teamLogic'
import { TRACING_SCENE_VIEWER_ID, TracingFilters, tracingFiltersLogic } from '../tracingFiltersLogic'
// Subset of TracingFilters persisted in a saved view. Compare-mode and the dragged overlay windows
// are ephemeral UI state tied to on-screen sparkline data, so they're intentionally excluded.
export type SavedTracingFilters = Pick<
TracingFilters,
'dateRange' | 'serviceNames' | 'filterGroup' | 'orderBy' | 'orderDirection' | 'viewMode'
>
// Handwritten until `insightscli build:openapi` generates `TracingViewApi` from TracingViewSerializer.
export interface TracingView {
id: string
short_id: string
name: string
filters: Partial<SavedTracingFilters>
pinned: boolean
created_at: string
created_by: {
first_name?: string
email?: string
} | null
updated_at: string | null
}
const tracingViewsUrl = (teamId: number | null): string => `v1/projects/${teamId}/tracing/views`
// Generated by kea-typegen. Update if you're an agent, ignore if you're human.
export interface tracingViewsLogicValues {
currentTeamId: number | null // teamLogic
views: TracingView[]
viewsLoading: boolean
}
// Generated by kea-typegen. Update if you're an agent, ignore if you're human.
export interface tracingViewsLogicActions {
setFilters: (filters: Partial<TracingFilters>) => {
filters: Partial<TracingFilters>
} // tracingFiltersLogic
createView: ({ name, filters }: { filters: Partial<SavedTracingFilters>; name: string }) => {
name: string
filters: Partial<SavedTracingFilters>
}
createViewFailure: (
error: string,
errorObject?: any
) => {
error: string
errorObject?: any
}
createViewSuccess: (
views: TracingView[],
payload?: {
name: string
filters: Partial<SavedTracingFilters>
}
) => {
views: TracingView[]
payload?: {
name: string
filters: Partial<SavedTracingFilters>
}
}
deleteView: (shortId: string) => {
shortId: string
}
loadView: (view: TracingView) => {
view: TracingView
}
loadViews: () => any
loadViewsFailure: (
error: string,
errorObject?: any
) => {
error: string
errorObject?: any
}
loadViewsSuccess: (
views: TracingView[],
payload?: any
) => {
views: TracingView[]
payload?: any
}
}
export type tracingViewsLogicType = MakeLogicType<tracingViewsLogicValues, tracingViewsLogicActions>
export const tracingViewsLogic = kea<tracingViewsLogicType>([
path(['products', 'tracing', 'frontend', 'savedViews', 'tracingViewsLogic']),
// Saved views are a /tracing scene feature — always bind the scene's viewer instance.
connect(() => ({
values: [teamLogic, ['currentTeamId']],
actions: [tracingFiltersLogic({ id: TRACING_SCENE_VIEWER_ID }), ['setFilters']],
})),
actions({
deleteView: (shortId: string) => ({ shortId }),
loadView: (view: TracingView) => ({ view }),
}),
loaders(({ values }) => ({
views: [
[] as TracingView[],
{
loadViews: async () => {
// nosemgrep: prefer-codegen-api
const response = await api.get(`${tracingViewsUrl(values.currentTeamId)}/`)
return response.results
},
createView: async ({ name, filters }: { name: string; filters: Partial<SavedTracingFilters> }) => {
// nosemgrep: prefer-codegen-api
const created: TracingView = await api.create(`${tracingViewsUrl(values.currentTeamId)}/`, {
name,
filters,
})
toast.success('View saved')
return [created, ...values.views]
},
},
],
})),
reducers({
views: {
deleteView: (state, { shortId }) => state.filter((v) => v.short_id !== shortId),
},
}),
listeners(({ actions, values }) => ({
deleteView: async ({ shortId }) => {
try {
// nosemgrep: prefer-codegen-api
await api.delete(`${tracingViewsUrl(values.currentTeamId)}/${shortId}/`)
toast.success('View deleted')
} catch {
toast.error('Failed to delete view')
actions.loadViews()
}
},
loadView: ({ view }) => {
actions.setFilters(view.filters || {})
},
createViewFailure: () => {
toast.error('Failed to save view')
},
loadViewsFailure: () => {
toast.error('Failed to load saved views')
},
})),
])