Allow editing an existing metric declaration in place (ticket 022)
Clicking a declared metric badge in Metrics.tsx now loads its regex/params
into the declaration form for editing (in addition to selecting it for the
bucket-values view), and shows those properties inline once selected rather
than only on hover. The backend gains PUT /api/metrics/{name}, backed by a
new engine::update_metric that reuses declare_metric's validation, requires
the metric to already exist, supports renaming, and re-backfills bucket data
from the new regex/params instead of leaving stale buckets from the old one.
Submitting the form while editing calls the update endpoint in place rather
than creating a second declaration; declaring a brand-new metric is unchanged.
Note: tickets/022-edit-existing-metric-declarations.md does not exist on this
worktree's branch (it was added to tickets/ on main after this branch was
created), so there is nothing to delete here.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XFUNZwCvz3kcod3352ZR2o
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
e6e5bc8f50
commit
56da086c7a
@@ -200,6 +200,28 @@ async function postJson<T>(path: string, body: unknown): Promise<T> {
|
||||
return res.json() as Promise<T>
|
||||
}
|
||||
|
||||
// Same error handling as `postJson` (throws `ApiError` with the parsed message) but for a PUT — used where updating in place, rather than creating, is the operation.
|
||||
async function putJson<T>(path: string, body: unknown): Promise<T> {
|
||||
const res = await fetch(path, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body),
|
||||
})
|
||||
if (!res.ok) {
|
||||
let message = `${path} failed: ${res.status}`
|
||||
try {
|
||||
const errorBody = (await res.json()) as Partial<ApiErrorBody>
|
||||
if (errorBody && typeof errorBody.error === 'string') {
|
||||
message = errorBody.error
|
||||
}
|
||||
} catch {
|
||||
// response body wasn't JSON; fall back to the generic message
|
||||
}
|
||||
throw new ApiError(message)
|
||||
}
|
||||
return res.json() as Promise<T>
|
||||
}
|
||||
|
||||
async function sendJson<T>(path: string, method: string, body?: unknown): Promise<T> {
|
||||
const res = await fetch(path, {
|
||||
method,
|
||||
@@ -270,6 +292,11 @@ export function declareMetric(request: MetricDeclaration): Promise<MetricDeclara
|
||||
return postJson('/api/metrics', request)
|
||||
}
|
||||
|
||||
// Updates the metric declaration named `existingName` in place (`existingName` may differ from `request.name` to rename it), rather than declaring a second metric or failing on a duplicate-name conflict.
|
||||
export function updateMetric(existingName: string, request: MetricDeclaration): Promise<MetricDeclaration> {
|
||||
return putJson(`/api/metrics/${encodeURIComponent(existingName)}`, request)
|
||||
}
|
||||
|
||||
export function fetchMetricBucketValues(
|
||||
name: string,
|
||||
params: {
|
||||
|
||||
Reference in New Issue
Block a user