Add DELETE /api/metrics/{name} to allow deleting a declared metric (ticket 051)
Wires the engine's existing RemoveMetricDeclaration message (previously only used internally by delete_profile's cleanup) up to a new DELETE route, persists the removal into the active profile so it survives a restart, and adds a delete affordance in the Metrics UI. Decisions: - Bucket-value history under the deleted metric's name is purged, not left orphaned (engine.rs's new remove_metric_declaration reuses update_metric's clear_metric_data helper). This is what lets a dashboard panel or PromQL query still referencing the metric by name fail with a readable "unknown metric" error instead of silently returning stale data. - evaluate_promql now errors when a metric has neither a declaration nor any bucket data, since a metric ingested via --metrics-url scraping never gets a MetricDeclaration but must stay queryable. Verified with a seeded fixture (promql_query_against_deleted_metric_fails_with_readable_error in src/api.rs). 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
d17cfe9b83
commit
466c81fa94
@@ -694,6 +694,27 @@ impl Settings {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Removes `name`'s persisted metric declaration from `profile_name` (ticket 051), so it doesn't
|
||||
/// reappear from `settings.json` on the next restart. A no-op (not an error) if the profile has
|
||||
/// no record under that name — matches `Settings::remove_css`'s sibling calls in spirit, but
|
||||
/// deleting an already-gone declaration is fine here since the caller (the API layer) has
|
||||
/// already confirmed against the live engine index whether the metric existed at all.
|
||||
pub fn remove_metric_declaration(
|
||||
&mut self,
|
||||
settings_path: &Path,
|
||||
profile_name: &str,
|
||||
name: &str,
|
||||
) -> Result<(), ProfileError> {
|
||||
let profile = self
|
||||
.profiles
|
||||
.iter_mut()
|
||||
.find(|p| p.name == profile_name)
|
||||
.ok_or(ProfileError::NotFound)?;
|
||||
profile.metrics.retain(|r| r.name != name);
|
||||
self.save(settings_path)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Lists the dashboards saved on `profile_name`; fails with `NotFound` if it doesn't exist.
|
||||
pub fn list_dashboards(&self, profile_name: &str) -> Result<&[Dashboard], ProfileError> {
|
||||
let profile = self.profile(profile_name).ok_or(ProfileError::NotFound)?;
|
||||
@@ -1036,6 +1057,28 @@ mod tests {
|
||||
assert_eq!(profile.metrics.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_metric_declaration_drops_it_and_does_not_reappear_after_restart() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let settings_path = dir.path().join("settings.json");
|
||||
let mut settings = Settings::default();
|
||||
|
||||
settings.create_profile(&settings_path, "web".to_string()).unwrap();
|
||||
let metric_req = metric_request("requests", r"duration=(?P<duration>\d+)", "duration");
|
||||
settings.record_metric_declaration(&settings_path, "web", &metric_req, &[]).unwrap();
|
||||
assert_eq!(settings.profile("web").unwrap().metrics.len(), 1);
|
||||
|
||||
settings.remove_metric_declaration(&settings_path, "web", "requests").unwrap();
|
||||
assert!(settings.profile("web").unwrap().metrics.is_empty());
|
||||
|
||||
// Removing an unknown metric name is a harmless no-op, not an error.
|
||||
settings.remove_metric_declaration(&settings_path, "web", "never-declared").unwrap();
|
||||
|
||||
// Reload from disk: the removal itself survives a restart (ticket 051's acceptance criterion).
|
||||
let reloaded = Settings::load(&settings_path);
|
||||
assert!(reloaded.profile("web").unwrap().metrics.is_empty());
|
||||
}
|
||||
|
||||
/// Acceptance criterion: declaring params/metrics, "restarting" (reloading settings from disk), and replaying into a fresh engine reproduces the same declarations and still ingests lines, without re-declaring by hand.
|
||||
#[tokio::test]
|
||||
async fn profile_round_trip_reproduces_declarations_in_fresh_engine() {
|
||||
|
||||
Reference in New Issue
Block a user