created get_metric_handler_version_query_param

This commit is contained in:
dal 2025-03-19 10:49:56 -06:00
parent 92caf4b7d4
commit 60ebcf79cd
No known key found for this signature in database
GPG Key ID: 16F4B0E1E9F61122
6 changed files with 78 additions and 33 deletions

View File

@ -89,10 +89,10 @@ pub async fn get_dashboard_handler(dashboard_id: &Uuid, user_id: &Uuid) -> Resul
}) })
.collect(); .collect();
// Fetch all metrics concurrently // Fetch all metrics concurrently (latest versions)
let metric_futures: Vec<_> = metric_ids let metric_futures: Vec<_> = metric_ids
.iter() .iter()
.map(|metric_id| get_metric_handler(metric_id, user_id)) .map(|metric_id| get_metric_handler(metric_id, user_id, None))
.collect(); .collect();
let metric_results = join_all(metric_futures).await; let metric_results = join_all(metric_futures).await;

View File

@ -103,8 +103,8 @@ pub async fn get_metric_data_handler(
let user_id = user.id; let user_id = user.id;
// Retrieve the metric definition // Retrieve the metric definition (latest version)
let metric = get_metric_handler(&request.metric_id, &user_id).await?; let metric = get_metric_handler(&request.metric_id, &user_id, None).await?;
// Parse the metric definition from YAML to get SQL and dataset IDs // Parse the metric definition from YAML to get SQL and dataset IDs
let metric_yml = serde_yaml::from_str::<MetricYml>(&metric.file)?; let metric_yml = serde_yaml::from_str::<MetricYml>(&metric.file)?;

View File

@ -48,8 +48,11 @@ struct UserInfo {
avatar_url: Option<String>, avatar_url: Option<String>,
} }
/// Handler to retrieve a metric by ID /// Handler to retrieve a metric by ID with optional version number
pub async fn get_metric_handler(metric_id: &Uuid, user_id: &Uuid) -> Result<BusterMetric> { ///
/// If version_number is provided, returns that specific version of the metric.
/// If version_number is None, returns the latest version of the metric.
pub async fn get_metric_handler(metric_id: &Uuid, user_id: &Uuid, version_number: Option<i32>) -> Result<BusterMetric> {
let mut conn = match get_pg_pool().get().await { let mut conn = match get_pg_pool().get().await {
Ok(conn) => conn, Ok(conn) => conn,
Err(e) => return Err(anyhow!("Failed to get database connection: {}", e)), Err(e) => return Err(anyhow!("Failed to get database connection: {}", e)),
@ -91,16 +94,38 @@ pub async fn get_metric_handler(metric_id: &Uuid, user_id: &Uuid) -> Result<Bust
} }
}); });
let metric_yml = metric_file.content.clone(); // Determine which version to use based on version_number parameter
let (metric_content, version_num) = if let Some(version) = version_number {
// Get the specific version if it exists
if let Some(v) = metric_file.version_history.get_version(version) {
match &v.content {
database::types::VersionContent::MetricYml(content) => (content.clone(), v.version_number),
_ => return Err(anyhow!("Invalid version content type"))
}
} else {
return Err(anyhow!("Version {} not found", version));
}
} else {
// Get the latest version
if let Some(v) = metric_file.version_history.get_latest_version() {
match &v.content {
database::types::VersionContent::MetricYml(content) => (content.clone(), v.version_number),
_ => return Err(anyhow!("Invalid version content type"))
}
} else {
// Fall back to current content if no version history
(metric_file.content.clone(), 1)
}
};
// Convert content to pretty YAML // Convert content to pretty YAML
let file = match serde_yaml::to_string(&metric_file.content) { let file = match serde_yaml::to_string(&metric_content) {
Ok(yaml) => yaml, Ok(yaml) => yaml,
Err(e) => return Err(anyhow!("Failed to convert content to YAML: {}", e)), Err(e) => return Err(anyhow!("Failed to convert content to YAML: {}", e)),
}; };
// Parse data metadata from MetricYml // Parse data metadata from the selected version's MetricYml
let data_metadata = metric_yml.data_metadata.map(|metadata| { let data_metadata = metric_content.data_metadata.map(|metadata| {
DataMetadata { DataMetadata {
column_count: metadata.len() as i32, column_count: metadata.len() as i32,
column_metadata: metadata column_metadata: metadata
@ -132,7 +157,7 @@ pub async fn get_metric_handler(metric_id: &Uuid, user_id: &Uuid) -> Result<Bust
// Get dataset information for all dataset IDs // Get dataset information for all dataset IDs
let mut datasets = Vec::new(); let mut datasets = Vec::new();
for dataset_id in &metric_yml.dataset_ids { for dataset_id in &metric_content.dataset_ids {
if let Ok(dataset_info) = datasets::table if let Ok(dataset_info) = datasets::table
.filter(datasets::id.eq(dataset_id)) .filter(datasets::id.eq(dataset_id))
.filter(datasets::deleted_at.is_null()) .filter(datasets::deleted_at.is_null())
@ -165,24 +190,19 @@ pub async fn get_metric_handler(metric_id: &Uuid, user_id: &Uuid) -> Result<Bust
}) })
.collect(); .collect();
// Get the latest version number from version history
let latest_version = metric_file.version_history.get_latest_version()
.map(|v| v.version_number)
.unwrap_or(1);
// Construct BusterMetric // Construct BusterMetric
Ok(BusterMetric { Ok(BusterMetric {
id: metric_file.id, id: metric_file.id,
metric_type: "metric".to_string(), metric_type: "metric".to_string(),
title: metric_file.name, title: metric_file.name,
version_number: latest_version, version_number: version_num,
description: metric_yml.description, description: metric_content.description,
file_name: metric_file.file_name, file_name: metric_file.file_name,
time_frame: metric_yml.time_frame, time_frame: metric_content.time_frame,
datasets, datasets,
data_source_id: "".to_string(), // This would need to be fetched from another source data_source_id: "".to_string(), // This would need to be fetched from another source
error: None, error: None,
chart_config: Some(metric_yml.chart_config), chart_config: Some(metric_content.chart_config),
data_metadata, data_metadata,
status: metric_file.verification, status: metric_file.verification,
evaluation_score, evaluation_score,

View File

@ -54,8 +54,8 @@ pub async fn update_metric_handler(
let mut conn = get_pg_pool().get().await let mut conn = get_pg_pool().get().await
.map_err(|e| anyhow!("Failed to get database connection: {}", e))?; .map_err(|e| anyhow!("Failed to get database connection: {}", e))?;
// Check if metric exists and user has access // Check if metric exists and user has access - use the latest version
let metric = get_metric_handler(metric_id, user_id).await?; let metric = get_metric_handler(metric_id, user_id, None).await?;
// If file is provided, it takes precedence over all other fields // If file is provided, it takes precedence over all other fields
let content = if let Some(file_content) = request.file { let content = if let Some(file_content) = request.file {
@ -134,8 +134,8 @@ pub async fn update_metric_handler(
.await .await
}.map_err(|e| anyhow!("Failed to update metric: {}", e))?; }.map_err(|e| anyhow!("Failed to update metric: {}", e))?;
// Return the updated metric // Return the updated metric - latest version
get_metric_handler(metric_id, user_id).await get_metric_handler(metric_id, user_id, None).await
} }
#[cfg(test)] #[cfg(test)]

View File

@ -1,28 +1,42 @@
use axum::{ use axum::{
extract::Path, extract::{Path, Query},
http::StatusCode, http::StatusCode,
Extension, Extension,
}; };
use handlers::metrics::{get_metric_handler, BusterMetric}; use handlers::metrics::{get_metric_handler, BusterMetric};
use serde::Deserialize;
use uuid::Uuid; use uuid::Uuid;
use middleware::AuthenticatedUser; use middleware::AuthenticatedUser;
use crate::routes::rest::ApiResponse; use crate::routes::rest::ApiResponse;
#[derive(Debug, Deserialize)]
pub struct GetMetricQueryParams {
#[serde(rename = "version_number")]
version_number: Option<i32>,
}
pub async fn get_metric_rest_handler( pub async fn get_metric_rest_handler(
Extension(user): Extension<AuthenticatedUser>, Extension(user): Extension<AuthenticatedUser>,
Path(id): Path<Uuid>, Path(id): Path<Uuid>,
Query(params): Query<GetMetricQueryParams>,
) -> Result<ApiResponse<BusterMetric>, (StatusCode, &'static str)> { ) -> Result<ApiResponse<BusterMetric>, (StatusCode, &'static str)> {
tracing::info!( tracing::info!(
"Processing GET request for metric with ID: {}, user_id: {}", "Processing GET request for metric with ID: {}, user_id: {}, version_number: {:?}",
id, id,
user.id user.id,
params.version_number
); );
let metric = match get_metric_handler(&id, &user.id).await { let metric = match get_metric_handler(&id, &user.id, params.version_number).await {
Ok(response) => response, Ok(response) => response,
Err(e) => { Err(e) => {
tracing::error!("Error getting metric: {}", e); tracing::error!("Error getting metric: {}", e);
let error_message = e.to_string();
// Return 404 if version not found, otherwise 500
if error_message.contains("Version") && error_message.contains("not found") {
return Err((StatusCode::NOT_FOUND, "Version not found"));
}
return Err((StatusCode::INTERNAL_SERVER_ERROR, "Failed to get metric")); return Err((StatusCode::INTERNAL_SERVER_ERROR, "Failed to get metric"));
} }
}; };

View File

@ -14,25 +14,36 @@ use crate::routes::ws::{
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct GetMetricWsRequest { pub struct GetMetricWsRequest {
pub id: Uuid, pub id: Uuid,
#[serde(rename = "version_number")]
pub version_number: Option<i32>,
} }
pub async fn get_metric(user: &AuthenticatedUser, request: GetMetricWsRequest) -> Result<()> { pub async fn get_metric(user: &AuthenticatedUser, request: GetMetricWsRequest) -> Result<()> {
tracing::info!( tracing::info!(
"Processing WebSocket GET request for metric with ID: {}, user_id: {}", "Processing WebSocket GET request for metric with ID: {}, user_id: {}, version_number: {:?}",
request.id, request.id,
user.id user.id,
request.version_number
); );
let metric = match get_metric_handler(&request.id, &user.id).await { let metric = match get_metric_handler(&request.id, &user.id, request.version_number).await {
Ok(metric) => metric, Ok(metric) => metric,
Err(e) => { Err(e) => {
tracing::error!("Error getting metric: {}", e); tracing::error!("Error getting metric: {}", e);
let error_message = e.to_string();
// Use appropriate error code based on the error
let error_code = if error_message.contains("Version") && error_message.contains("not found") {
WsErrorCode::NotFound
} else {
WsErrorCode::InternalServerError
};
send_error_message( send_error_message(
&user.id.to_string(), &user.id.to_string(),
WsRoutes::Metrics(MetricRoute::Get), WsRoutes::Metrics(MetricRoute::Get),
WsEvent::Metrics(MetricEvent::GetMetric), WsEvent::Metrics(MetricEvent::GetMetric),
WsErrorCode::InternalServerError, error_code,
"Failed to get metric.".to_string(), format!("Failed to get metric: {}", error_message),
user, user,
) )
.await?; .await?;