dev: lay ground for sending filters via get params

This commit is contained in:
Jeremy Wall 2024-03-12 20:33:05 -04:00
parent 02314a3309
commit cef7b42fac
4 changed files with 38 additions and 6 deletions

View File

@ -1,3 +1,4 @@
use std::collections::HashMap;
// Copyright 2023 Jeremy Wall // Copyright 2023 Jeremy Wall
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
@ -120,12 +121,13 @@ pub struct LogStream {
pub query_type: QueryType, pub query_type: QueryType,
} }
pub async fn prom_query_data( pub async fn prom_query_data<'a>(
graph: &Graph, graph: &Graph,
dash: &Dashboard, dash: &Dashboard,
query_span: Option<GraphSpan>, query_span: Option<GraphSpan>,
filters: &Option<HashMap<&'a str, &'a str>>,
) -> Result<Vec<QueryResult>> { ) -> Result<Vec<QueryResult>> {
let connections = graph.get_query_connections(&dash.span, &query_span); let connections = graph.get_query_connections(&dash.span, &query_span, filters);
let mut data = Vec::new(); let mut data = Vec::new();
for conn in connections { for conn in connections {
data.push(prom_to_samples( data.push(prom_to_samples(
@ -205,12 +207,14 @@ impl Graph {
&'graph self, &'graph self,
graph_span: &'graph Option<GraphSpan>, graph_span: &'graph Option<GraphSpan>,
query_span: &'graph Option<GraphSpan>, query_span: &'graph Option<GraphSpan>,
filters: &'graph Option<HashMap<&'graph str, &'graph str>>,
) -> Vec<PromQueryConn<'conn>> { ) -> Vec<PromQueryConn<'conn>> {
let mut conns = Vec::new(); let mut conns = Vec::new();
for plot in self.plots.iter() { for plot in self.plots.iter() {
debug!( debug!(
query = plot.query, query = plot.query,
source = plot.source, source = plot.source,
filters = ?filters,
"Getting query connection for graph", "Getting query connection for graph",
); );
let mut conn = PromQueryConn::new( let mut conn = PromQueryConn::new(
@ -219,6 +223,9 @@ impl Graph {
self.query_type.clone(), self.query_type.clone(),
plot.meta.clone(), plot.meta.clone(),
); );
if let Some(filters) = filters {
conn = conn.with_filters(filters);
}
// Query params take precendence over all other settings. Then graph settings take // Query params take precendence over all other settings. Then graph settings take
// precedences and finally the dashboard settings take precendence // precedences and finally the dashboard settings take precendence
if let Some((end, duration, step_duration)) = graph_span_to_tuple(query_span) { if let Some((end, duration, step_duration)) = graph_span_to_tuple(query_span) {

View File

@ -53,7 +53,7 @@ struct Cli {
async fn validate(dash: &Dashboard) -> anyhow::Result<()> { async fn validate(dash: &Dashboard) -> anyhow::Result<()> {
if let Some(ref graphs) = dash.graphs { if let Some(ref graphs) = dash.graphs {
for graph in graphs.iter() { for graph in graphs.iter() {
let data = prom_query_data(graph, &dash, None).await; let data = prom_query_data(graph, &dash, None, &None).await;
if data.is_err() { if data.is_err() {
error!(err=?data, "Invalid dashboard graph query or queries"); error!(err=?data, "Invalid dashboard graph query or queries");
} }

View File

@ -30,6 +30,7 @@ pub struct PromQueryConn<'conn> {
query: &'conn str, query: &'conn str,
span: Option<TimeSpan>, span: Option<TimeSpan>,
query_type: QueryType, query_type: QueryType,
filters: Option<&'conn HashMap<&'conn str, &'conn str>>,
pub meta: PlotMeta, pub meta: PlotMeta,
} }
@ -46,9 +47,16 @@ impl<'conn> PromQueryConn<'conn> {
query_type, query_type,
meta, meta,
span: None, span: None,
filters: None,
} }
} }
pub fn with_filters(mut self, filters: &'conn HashMap<&'conn str, &'conn str>) -> Self {
self.filters = Some(filters);
self
}
pub fn with_span( pub fn with_span(
mut self, mut self,
end: DateTime<Utc>, end: DateTime<Utc>,

View File

@ -54,7 +54,7 @@ pub async fn loki_query(
.expect("No logs in this dashboard") .expect("No logs in this dashboard")
.get(loki_idx) .get(loki_idx)
.expect(&format!("No such log query {}", loki_idx)); .expect(&format!("No such log query {}", loki_idx));
let plots = vec![loki_query_data(log, dash, query_to_graph_span(query)) let plots = vec![loki_query_data(log, dash, query_to_graph_span(&query))
.await .await
.expect("Unable to get log query results")]; .expect("Unable to get log query results")];
Json(GraphPayload { Json(GraphPayload {
@ -79,7 +79,8 @@ pub async fn graph_query(
.expect("No graphs in this dashboard") .expect("No graphs in this dashboard")
.get(graph_idx) .get(graph_idx)
.expect(&format!("No such graph in dasboard {}", dash_idx)); .expect(&format!("No such graph in dasboard {}", dash_idx));
let plots = prom_query_data(graph, dash, query_to_graph_span(query)) let filters = query_to_filterset(&query);
let plots = prom_query_data(graph, dash, query_to_graph_span(&query), &filters)
.await .await
.expect("Unable to get query results"); .expect("Unable to get query results");
Json(GraphPayload { Json(GraphPayload {
@ -89,7 +90,23 @@ pub async fn graph_query(
}) })
} }
fn query_to_graph_span(query: HashMap<String, String>) -> Option<GraphSpan> { fn query_to_filterset<'v, 'a: 'v>(query: &'a HashMap<String, String>) -> Option<HashMap<&'v str, &'v str>> {
let mut label_set = HashMap::new();
for (k, v) in query.iter() {
if k.starts_with("filter-") {
if let Some(label) = k.strip_prefix("filter-") {
label_set.insert(label, v.as_str());
}
}
}
if label_set.is_empty() {
None
} else {
Some(label_set)
}
}
fn query_to_graph_span<'a>(query: &'a HashMap<String, String>) -> Option<GraphSpan> {
let query_span = { let query_span = {
if query.contains_key("end") if query.contains_key("end")
&& query.contains_key("duration") && query.contains_key("duration")