mirror of https://github.com/buster-so/buster.git
added in the user avatar url
This commit is contained in:
parent
b466a061c4
commit
bba0e30b67
|
@ -365,6 +365,7 @@ pub struct User {
|
||||||
pub created_at: DateTime<Utc>,
|
pub created_at: DateTime<Utc>,
|
||||||
pub updated_at: DateTime<Utc>,
|
pub updated_at: DateTime<Utc>,
|
||||||
pub attributes: Value,
|
pub attributes: Value,
|
||||||
|
pub avatar_url: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
|
|
|
@ -564,6 +564,7 @@ diesel::table! {
|
||||||
created_at -> Timestamptz,
|
created_at -> Timestamptz,
|
||||||
updated_at -> Timestamptz,
|
updated_at -> Timestamptz,
|
||||||
attributes -> Jsonb,
|
attributes -> Jsonb,
|
||||||
|
avatar_url -> Nullable<Text>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,10 +29,17 @@ pub struct ChatListItem {
|
||||||
pub last_edited: String,
|
pub last_edited: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct PaginationInfo {
|
||||||
|
pub has_more: bool,
|
||||||
|
pub next_page_token: Option<String>,
|
||||||
|
pub total_items: i32, // Number of items in current page
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct ListChatsResponse {
|
pub struct ListChatsResponse {
|
||||||
pub items: Vec<ChatListItem>,
|
pub items: Vec<ChatListItem>,
|
||||||
pub next_page_token: Option<String>,
|
pub pagination: PaginationInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Queryable)]
|
#[derive(Queryable)]
|
||||||
|
@ -76,11 +83,8 @@ pub async fn list_chats_handler(
|
||||||
|
|
||||||
// Add cursor-based pagination if page_token is provided
|
// Add cursor-based pagination if page_token is provided
|
||||||
if let Some(token) = request.page_token {
|
if let Some(token) = request.page_token {
|
||||||
let decoded_token = base64::decode(&token)
|
// Parse the RFC3339 timestamp directly
|
||||||
.map_err(|_| anyhow!("Invalid page token"))?;
|
let cursor_dt = DateTime::parse_from_rfc3339(&token)
|
||||||
let cursor_timestamp = String::from_utf8(decoded_token)
|
|
||||||
.map_err(|_| anyhow!("Invalid page token format"))?;
|
|
||||||
let cursor_dt = DateTime::parse_from_rfc3339(&cursor_timestamp)
|
|
||||||
.map_err(|_| anyhow!("Invalid timestamp in page token"))?
|
.map_err(|_| anyhow!("Invalid timestamp in page token"))?
|
||||||
.with_timezone(&Utc);
|
.with_timezone(&Utc);
|
||||||
|
|
||||||
|
@ -106,9 +110,9 @@ pub async fn list_chats_handler(
|
||||||
.load::<ChatWithUser>(&mut conn)
|
.load::<ChatWithUser>(&mut conn)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Check if there are more results
|
// Check if there are more results and prepare pagination info
|
||||||
let has_more = results.len() > request.page_size as usize;
|
let has_more = results.len() > request.page_size as usize;
|
||||||
let mut items = results
|
let items: Vec<ChatListItem> = results
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.take(request.page_size as usize)
|
.take(request.page_size as usize)
|
||||||
.map(|chat| {
|
.map(|chat| {
|
||||||
|
@ -130,19 +134,22 @@ pub async fn list_chats_handler(
|
||||||
last_edited: chat.updated_at.to_rfc3339(),
|
last_edited: chat.updated_at.to_rfc3339(),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect();
|
||||||
|
|
||||||
// Generate next page token if there are more results
|
// Create pagination info
|
||||||
let next_page_token = if has_more {
|
let pagination = PaginationInfo {
|
||||||
items
|
has_more,
|
||||||
.last()
|
next_page_token: if has_more {
|
||||||
.map(|last_item| base64::encode(&last_item.created_at))
|
// Just use the RFC3339 timestamp directly as the token
|
||||||
|
items.last().map(|last_item| last_item.created_at.clone())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
},
|
||||||
|
total_items: items.len() as i32,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(ListChatsResponse {
|
Ok(ListChatsResponse {
|
||||||
items,
|
items,
|
||||||
next_page_token,
|
pagination,
|
||||||
})
|
})
|
||||||
}
|
}
|
|
@ -10,7 +10,7 @@ use serde_yaml;
|
||||||
use crate::files::dashboard_files::types::{
|
use crate::files::dashboard_files::types::{
|
||||||
BusterDashboard, BusterDashboardResponse, DashboardConfig, DashboardRow, DashboardRowItem,
|
BusterDashboard, BusterDashboardResponse, DashboardConfig, DashboardRow, DashboardRowItem,
|
||||||
};
|
};
|
||||||
use crate::files::metric_files::helpers::get_metric;
|
use crate::metrics::get_metric_handler;
|
||||||
use database::enums::{AssetPermissionRole, Verification};
|
use database::enums::{AssetPermissionRole, Verification};
|
||||||
use database::pool::get_pg_pool;
|
use database::pool::get_pg_pool;
|
||||||
use database::schema::dashboard_files;
|
use database::schema::dashboard_files;
|
||||||
|
@ -90,7 +90,7 @@ pub async fn get_dashboard(dashboard_id: &Uuid, user_id: &Uuid) -> Result<Buster
|
||||||
// Fetch all metrics concurrently
|
// Fetch all metrics concurrently
|
||||||
let metric_futures: Vec<_> = metric_ids
|
let metric_futures: Vec<_> = metric_ids
|
||||||
.iter()
|
.iter()
|
||||||
.map(|metric_id| get_metric(metric_id, user_id))
|
.map(|metric_id| get_metric_handler(metric_id, user_id))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let metric_results = join_all(metric_futures).await;
|
let metric_results = join_all(metric_futures).await;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use database::enums::{AssetPermissionRole, Verification};
|
use database::enums::{AssetPermissionRole, Verification};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::files::BusterMetric;
|
use crate::metrics::types::BusterMetric;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct BusterDashboardListItem {
|
pub struct BusterDashboardListItem {
|
||||||
|
|
|
@ -1,153 +0,0 @@
|
||||||
use anyhow::{anyhow, Result};
|
|
||||||
use diesel::{ExpressionMethods, QueryDsl, Queryable, Selectable};
|
|
||||||
use diesel_async::RunQueryDsl;
|
|
||||||
use serde_json::{json, Value};
|
|
||||||
use uuid::Uuid;
|
|
||||||
use serde_yaml;
|
|
||||||
|
|
||||||
use crate::files::metric_files::types::BusterMetric;
|
|
||||||
use crate::files::{ColumnMetaData, ColumnType, DataMetadata, MinMaxValue, SimpleType};
|
|
||||||
use database::enums::Verification;
|
|
||||||
use database::pool::get_pg_pool;
|
|
||||||
use database::schema::metric_files;
|
|
||||||
|
|
||||||
#[derive(Queryable, Selectable)]
|
|
||||||
#[diesel(table_name = metric_files)]
|
|
||||||
struct QueryableMetricFile {
|
|
||||||
id: Uuid,
|
|
||||||
name: String,
|
|
||||||
file_name: String,
|
|
||||||
content: Value,
|
|
||||||
verification: Verification,
|
|
||||||
evaluation_obj: Option<Value>,
|
|
||||||
evaluation_summary: Option<String>,
|
|
||||||
evaluation_score: Option<f64>,
|
|
||||||
created_by: Uuid,
|
|
||||||
created_at: chrono::DateTime<chrono::Utc>,
|
|
||||||
updated_at: chrono::DateTime<chrono::Utc>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_metric(metric_id: &Uuid, user_id: &Uuid) -> Result<BusterMetric> {
|
|
||||||
let mut conn = match get_pg_pool().get().await {
|
|
||||||
Ok(conn) => conn,
|
|
||||||
Err(e) => return Err(anyhow!("Failed to get database connection: {}", e)),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Query the metric file
|
|
||||||
let metric_file = metric_files::table
|
|
||||||
.filter(metric_files::id.eq(metric_id))
|
|
||||||
.filter(metric_files::deleted_at.is_null())
|
|
||||||
.select((
|
|
||||||
metric_files::id,
|
|
||||||
metric_files::name,
|
|
||||||
metric_files::file_name,
|
|
||||||
metric_files::content,
|
|
||||||
metric_files::verification,
|
|
||||||
metric_files::evaluation_obj,
|
|
||||||
metric_files::evaluation_summary,
|
|
||||||
metric_files::evaluation_score,
|
|
||||||
metric_files::created_by,
|
|
||||||
metric_files::created_at,
|
|
||||||
metric_files::updated_at,
|
|
||||||
))
|
|
||||||
.first::<QueryableMetricFile>(&mut conn)
|
|
||||||
.await
|
|
||||||
.map_err(|e| match e {
|
|
||||||
diesel::result::Error::NotFound => anyhow!("Metric file not found or unauthorized"),
|
|
||||||
_ => anyhow!("Database error: {}", e),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Extract fields directly from the JSON content
|
|
||||||
let content = &metric_file.content;
|
|
||||||
|
|
||||||
let title = content
|
|
||||||
.get("title")
|
|
||||||
.and_then(Value::as_str)
|
|
||||||
.unwrap_or("Untitled")
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
let description = content
|
|
||||||
.get("description")
|
|
||||||
.and_then(|v| match v {
|
|
||||||
Value::Null => None,
|
|
||||||
v => v.as_str().map(String::from),
|
|
||||||
});
|
|
||||||
|
|
||||||
let sql = content
|
|
||||||
.get("sql")
|
|
||||||
.and_then(Value::as_str)
|
|
||||||
.unwrap_or_default()
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
// Get chart config directly
|
|
||||||
let chart_config = content.get("chart_config").cloned().unwrap_or(json!({}));
|
|
||||||
|
|
||||||
// Parse data metadata if it exists
|
|
||||||
let data_metadata = content.get("data_metadata").map(|metadata| {
|
|
||||||
DataMetadata {
|
|
||||||
column_count: metadata.as_array().map(|arr| arr.len() as i32).unwrap_or(1),
|
|
||||||
column_metadata: metadata
|
|
||||||
.as_array()
|
|
||||||
.map(|columns| {
|
|
||||||
columns
|
|
||||||
.iter()
|
|
||||||
.map(|col| ColumnMetaData {
|
|
||||||
name: col
|
|
||||||
.get("name")
|
|
||||||
.and_then(Value::as_str)
|
|
||||||
.unwrap_or("unknown")
|
|
||||||
.to_string(),
|
|
||||||
min_value: MinMaxValue::Number(0.0), // Default value
|
|
||||||
max_value: MinMaxValue::Number(0.0), // Default value
|
|
||||||
unique_values: 0, // Default value
|
|
||||||
simple_type: match col.get("data_type").and_then(Value::as_str) {
|
|
||||||
Some("string") => SimpleType::Text,
|
|
||||||
Some("number") => SimpleType::Number,
|
|
||||||
Some("boolean") => SimpleType::Boolean,
|
|
||||||
Some("date") => SimpleType::Date,
|
|
||||||
_ => SimpleType::Number,
|
|
||||||
},
|
|
||||||
column_type: match col.get("data_type").and_then(Value::as_str) {
|
|
||||||
Some("string") => ColumnType::Text,
|
|
||||||
Some("number") => ColumnType::Number,
|
|
||||||
Some("boolean") => ColumnType::Boolean,
|
|
||||||
Some("date") => ColumnType::Date,
|
|
||||||
_ => ColumnType::Number,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
.unwrap_or_default(),
|
|
||||||
row_count: 1, // Default value since it's not in your JSON structure
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Construct BusterMetric
|
|
||||||
Ok(BusterMetric {
|
|
||||||
id: metric_file.id.to_string(),
|
|
||||||
metric_type: "metric".to_string(),
|
|
||||||
title,
|
|
||||||
version_number: 1,
|
|
||||||
description,
|
|
||||||
file_name: metric_file.file_name,
|
|
||||||
time_frame: "TODO".to_string(),
|
|
||||||
dataset_id: "TODO".to_string(),
|
|
||||||
data_source_id: "TODO".to_string(),
|
|
||||||
dataset_name: None,
|
|
||||||
error: None,
|
|
||||||
chart_config: Some(chart_config),
|
|
||||||
data_metadata,
|
|
||||||
status: metric_file.verification,
|
|
||||||
evaluation_score: metric_file.evaluation_score.map(|score| score.to_string()),
|
|
||||||
evaluation_summary: metric_file.evaluation_summary.unwrap_or_default(),
|
|
||||||
file: serde_json::to_string(&content).unwrap_or_default(),
|
|
||||||
created_at: metric_file.created_at.to_string(),
|
|
||||||
updated_at: metric_file.updated_at.to_string(),
|
|
||||||
sent_by_id: metric_file.created_by.to_string(),
|
|
||||||
sent_by_name: "".to_string(),
|
|
||||||
sent_by_avatar_url: None,
|
|
||||||
code: None,
|
|
||||||
dashboards: vec![],
|
|
||||||
collections: vec![],
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
pub mod get_metric;
|
|
||||||
|
|
||||||
pub use get_metric::*;
|
|
|
@ -1,5 +0,0 @@
|
||||||
pub mod types;
|
|
||||||
pub mod helpers;
|
|
||||||
|
|
||||||
pub use types::*;
|
|
||||||
pub use helpers::*;
|
|
|
@ -1,5 +1,3 @@
|
||||||
pub mod metric_files;
|
|
||||||
pub mod dashboard_files;
|
pub mod dashboard_files;
|
||||||
|
|
||||||
pub use metric_files::*;
|
|
||||||
pub use dashboard_files::*;
|
pub use dashboard_files::*;
|
|
@ -5,10 +5,11 @@ use indexmap::IndexMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::files::metric_files::get_metric;
|
|
||||||
use query_engine::data_source_helpers;
|
use query_engine::data_source_helpers;
|
||||||
use query_engine::data_types::DataType;
|
use query_engine::data_types::DataType;
|
||||||
|
|
||||||
|
use crate::metrics::get_metric_handler;
|
||||||
|
|
||||||
/// Request structure for the get_metric_data handler
|
/// Request structure for the get_metric_data handler
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct GetMetricDataRequest {
|
pub struct GetMetricDataRequest {
|
||||||
|
@ -36,7 +37,7 @@ 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
|
||||||
let metric = get_metric(&request.metric_id, &user_id).await?;
|
let metric = get_metric_handler(&request.metric_id, &user_id).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_json::from_str::<MetricYml>(&metric.file)?;
|
let metric_yml = serde_json::from_str::<MetricYml>(&metric.file)?;
|
||||||
|
|
|
@ -0,0 +1,186 @@
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use diesel::{
|
||||||
|
ExpressionMethods, NullableExpressionMethods, QueryDsl, Queryable, Selectable, SelectableHelper,
|
||||||
|
};
|
||||||
|
use diesel_async::RunQueryDsl;
|
||||||
|
use serde_json::{json, Value};
|
||||||
|
use serde_yaml;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::metrics::types::{
|
||||||
|
BusterMetric, ColumnMetaData, ColumnType, DataMetadata, Dataset, MinMaxValue, SimpleType,
|
||||||
|
};
|
||||||
|
use agents::tools::file_tools::file_types::metric_yml::MetricYml;
|
||||||
|
use database::enums::Verification;
|
||||||
|
use database::pool::get_pg_pool;
|
||||||
|
use database::schema::{datasets, metric_files, users};
|
||||||
|
|
||||||
|
#[derive(Queryable, Selectable)]
|
||||||
|
#[diesel(table_name = metric_files)]
|
||||||
|
struct QueryableMetricFile {
|
||||||
|
id: Uuid,
|
||||||
|
name: String,
|
||||||
|
file_name: String,
|
||||||
|
content: Value,
|
||||||
|
verification: Verification,
|
||||||
|
evaluation_obj: Option<Value>,
|
||||||
|
evaluation_summary: Option<String>,
|
||||||
|
evaluation_score: Option<f64>,
|
||||||
|
created_by: Uuid,
|
||||||
|
created_at: chrono::DateTime<chrono::Utc>,
|
||||||
|
updated_at: chrono::DateTime<chrono::Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Queryable)]
|
||||||
|
struct DatasetInfo {
|
||||||
|
id: Uuid,
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Queryable)]
|
||||||
|
#[diesel(table_name = users)]
|
||||||
|
struct UserInfo {
|
||||||
|
#[diesel(sql_type = diesel::sql_types::Nullable<diesel::sql_types::Text>)]
|
||||||
|
name: Option<String>,
|
||||||
|
#[diesel(sql_type = diesel::sql_types::Nullable<diesel::sql_types::Text>)]
|
||||||
|
avatar_url: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handler to retrieve a metric by ID
|
||||||
|
pub async fn get_metric_handler(metric_id: &Uuid, user_id: &Uuid) -> Result<BusterMetric> {
|
||||||
|
let mut conn = match get_pg_pool().get().await {
|
||||||
|
Ok(conn) => conn,
|
||||||
|
Err(e) => return Err(anyhow!("Failed to get database connection: {}", e)),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Query the metric file
|
||||||
|
let metric_file = metric_files::table
|
||||||
|
.filter(metric_files::id.eq(metric_id))
|
||||||
|
.filter(metric_files::deleted_at.is_null())
|
||||||
|
.select((
|
||||||
|
metric_files::id,
|
||||||
|
metric_files::name,
|
||||||
|
metric_files::file_name,
|
||||||
|
metric_files::content,
|
||||||
|
metric_files::verification,
|
||||||
|
metric_files::evaluation_obj,
|
||||||
|
metric_files::evaluation_summary,
|
||||||
|
metric_files::evaluation_score,
|
||||||
|
metric_files::created_by,
|
||||||
|
metric_files::created_at,
|
||||||
|
metric_files::updated_at,
|
||||||
|
))
|
||||||
|
.first::<QueryableMetricFile>(&mut conn)
|
||||||
|
.await
|
||||||
|
.map_err(|e| match e {
|
||||||
|
diesel::result::Error::NotFound => anyhow!("Metric file not found or unauthorized"),
|
||||||
|
_ => anyhow!("Database error: {}", e),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Parse the content as MetricYml
|
||||||
|
let metric_yml: MetricYml = serde_json::from_value(metric_file.content.clone())?;
|
||||||
|
|
||||||
|
// Map evaluation score to High/Moderate/Low
|
||||||
|
let evaluation_score = metric_file.evaluation_score.map(|score| {
|
||||||
|
if score >= 0.8 {
|
||||||
|
"High".to_string()
|
||||||
|
} else if score >= 0.5 {
|
||||||
|
"Moderate".to_string()
|
||||||
|
} else {
|
||||||
|
"Low".to_string()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Convert content to pretty YAML
|
||||||
|
let file = match serde_yaml::to_string(&metric_file.content) {
|
||||||
|
Ok(yaml) => yaml,
|
||||||
|
Err(e) => return Err(anyhow!("Failed to convert content to YAML: {}", e)),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parse data metadata from MetricYml
|
||||||
|
let data_metadata = metric_yml.data_metadata.map(|metadata| {
|
||||||
|
DataMetadata {
|
||||||
|
column_count: metadata.len() as i32,
|
||||||
|
column_metadata: metadata
|
||||||
|
.iter()
|
||||||
|
.map(|col| ColumnMetaData {
|
||||||
|
name: col.name.clone(),
|
||||||
|
min_value: MinMaxValue::Number(0.0), // Default value
|
||||||
|
max_value: MinMaxValue::Number(0.0), // Default value
|
||||||
|
unique_values: 0, // Default value
|
||||||
|
simple_type: match col.data_type.as_str() {
|
||||||
|
"string" => SimpleType::Text,
|
||||||
|
"number" => SimpleType::Number,
|
||||||
|
"boolean" => SimpleType::Boolean,
|
||||||
|
"date" => SimpleType::Date,
|
||||||
|
_ => SimpleType::Text,
|
||||||
|
},
|
||||||
|
column_type: match col.data_type.as_str() {
|
||||||
|
"string" => ColumnType::Text,
|
||||||
|
"number" => ColumnType::Number,
|
||||||
|
"boolean" => ColumnType::Boolean,
|
||||||
|
"date" => ColumnType::Date,
|
||||||
|
_ => ColumnType::Text,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
row_count: 1, // Default value since it's not in the MetricYml structure
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get dataset information for all dataset IDs
|
||||||
|
let mut datasets = Vec::new();
|
||||||
|
for dataset_id in &metric_yml.dataset_ids {
|
||||||
|
if let Ok(dataset_info) = datasets::table
|
||||||
|
.filter(datasets::id.eq(dataset_id))
|
||||||
|
.filter(datasets::deleted_at.is_null())
|
||||||
|
.select((datasets::id, datasets::name))
|
||||||
|
.first::<DatasetInfo>(&mut conn)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
datasets.push(Dataset {
|
||||||
|
id: dataset_info.id.to_string(),
|
||||||
|
name: dataset_info.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get user information
|
||||||
|
let user_info = users::table
|
||||||
|
.filter(users::id.eq(metric_file.created_by))
|
||||||
|
.select((users::name, users::avatar_url))
|
||||||
|
.first::<UserInfo>(&mut conn)
|
||||||
|
.await
|
||||||
|
.map_err(|e| anyhow!("Failed to get user information: {}", e))?;
|
||||||
|
|
||||||
|
// Construct BusterMetric
|
||||||
|
Ok(BusterMetric {
|
||||||
|
id: metric_file.id.to_string(),
|
||||||
|
metric_type: "metric".to_string(),
|
||||||
|
title: metric_yml.title,
|
||||||
|
version_number: 1,
|
||||||
|
description: metric_yml.description,
|
||||||
|
file_name: metric_file.file_name,
|
||||||
|
time_frame: metric_yml
|
||||||
|
.updated_at
|
||||||
|
.map(|dt| dt.to_rfc3339())
|
||||||
|
.unwrap_or_else(|| "".to_string()),
|
||||||
|
datasets,
|
||||||
|
data_source_id: "".to_string(), // This would need to be fetched from another source
|
||||||
|
error: None,
|
||||||
|
chart_config: Some(serde_json::to_value(&metric_yml.chart_config)?),
|
||||||
|
data_metadata,
|
||||||
|
status: metric_file.verification,
|
||||||
|
evaluation_score,
|
||||||
|
evaluation_summary: metric_file.evaluation_summary.unwrap_or_default(),
|
||||||
|
file,
|
||||||
|
created_at: metric_file.created_at.to_rfc3339(),
|
||||||
|
updated_at: metric_file.updated_at.to_rfc3339(),
|
||||||
|
sent_by_id: metric_file.created_by.to_string(),
|
||||||
|
sent_by_name: user_info.name.unwrap_or("".to_string()),
|
||||||
|
sent_by_avatar_url: user_info.avatar_url,
|
||||||
|
code: None,
|
||||||
|
dashboards: vec![], // TODO: Get associated dashboards
|
||||||
|
collections: vec![], // TODO: Get associated collections
|
||||||
|
})
|
||||||
|
}
|
|
@ -1,3 +1,7 @@
|
||||||
pub mod get_metric_data_handler;
|
pub mod get_metric_data_handler;
|
||||||
|
pub mod get_metric_handler;
|
||||||
|
pub mod types;
|
||||||
|
|
||||||
pub use get_metric_data_handler::*;
|
pub use get_metric_data_handler::*;
|
||||||
|
pub use get_metric_handler::*;
|
||||||
|
pub use types::*;
|
|
@ -3,37 +3,29 @@ use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
// Note: BusterChartConfigProps needs to be defined
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
// #[derive(Debug, Serialize, Deserialize, Clone)]
|
pub struct Dataset {
|
||||||
// pub struct BusterChartConfigProps { ... }
|
pub name: String,
|
||||||
|
pub id: String,
|
||||||
// Note: VerificationStatus needs to be defined
|
}
|
||||||
// #[derive(Debug, Serialize, Deserialize, Clone)]
|
|
||||||
// pub enum VerificationStatus { ... }
|
|
||||||
|
|
||||||
// Note: BusterShare needs to be defined
|
|
||||||
// #[derive(Debug, Serialize, Deserialize, Clone)]
|
|
||||||
// pub struct BusterShare { ... }
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct BusterMetric {
|
pub struct BusterMetric {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub metric_type: String, // Assuming always "metric"
|
pub metric_type: String, // Always "metric"
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub version_number: i32,
|
pub version_number: i32,
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub file_name: String,
|
pub file_name: String,
|
||||||
pub time_frame: String,
|
pub time_frame: String,
|
||||||
pub dataset_id: String,
|
pub datasets: Vec<Dataset>,
|
||||||
pub data_source_id: String,
|
pub data_source_id: String,
|
||||||
pub dataset_name: Option<String>,
|
|
||||||
pub error: Option<String>,
|
pub error: Option<String>,
|
||||||
pub chart_config: Option<Value>, // Needs to be defined
|
pub chart_config: Option<Value>, // BusterChartConfigProps
|
||||||
pub data_metadata: Option<DataMetadata>,
|
pub data_metadata: Option<DataMetadata>,
|
||||||
pub status: Verification,
|
pub status: Verification,
|
||||||
#[serde(rename = "evaluation_score")]
|
pub evaluation_score: Option<String>, // "Moderate" | "High" | "Low"
|
||||||
pub evaluation_score: Option<String>,
|
|
||||||
pub evaluation_summary: String,
|
pub evaluation_summary: String,
|
||||||
pub file: String, // yaml file
|
pub file: String, // yaml file
|
||||||
pub created_at: String,
|
pub created_at: String,
|
||||||
|
@ -44,7 +36,6 @@ pub struct BusterMetric {
|
||||||
pub code: Option<String>,
|
pub code: Option<String>,
|
||||||
pub dashboards: Vec<Dashboard>,
|
pub dashboards: Vec<Dashboard>,
|
||||||
pub collections: Vec<Collection>,
|
pub collections: Vec<Collection>,
|
||||||
// BusterShare fields would be included here
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
@ -99,6 +90,7 @@ pub enum SimpleType {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum ColumnType {
|
pub enum ColumnType {
|
||||||
Text,
|
Text,
|
||||||
Float,
|
Float,
|
|
@ -0,0 +1,3 @@
|
||||||
|
-- This file should undo anything in `up.sql`
|
||||||
|
ALTER TABLE users
|
||||||
|
DROP COLUMN avatar_url;
|
|
@ -0,0 +1,3 @@
|
||||||
|
-- Your SQL goes here
|
||||||
|
ALTER TABLE users
|
||||||
|
ADD COLUMN avatar_url TEXT NULL;
|
|
@ -1,17 +1,25 @@
|
||||||
|
use axum::{
|
||||||
|
extract::Path,
|
||||||
|
http::StatusCode,
|
||||||
|
Extension,
|
||||||
|
};
|
||||||
use database::models::User;
|
use database::models::User;
|
||||||
use crate::routes::rest::ApiResponse;
|
use handlers::metrics::{get_metric_handler, BusterMetric};
|
||||||
use axum::extract::Path;
|
|
||||||
use axum::http::StatusCode;
|
|
||||||
use axum::Extension;
|
|
||||||
use handlers::files::metric_files::types::BusterMetric;
|
|
||||||
use handlers::files::metric_files::helpers::get_metric::get_metric;
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::routes::rest::ApiResponse;
|
||||||
|
|
||||||
pub async fn get_metric_rest_handler(
|
pub async fn get_metric_rest_handler(
|
||||||
Extension(user): Extension<User>,
|
Extension(user): Extension<User>,
|
||||||
Path(id): Path<Uuid>,
|
Path(id): Path<Uuid>,
|
||||||
) -> Result<ApiResponse<BusterMetric>, (StatusCode, &'static str)> {
|
) -> Result<ApiResponse<BusterMetric>, (StatusCode, &'static str)> {
|
||||||
let metric = match get_metric(&id, &user.id).await {
|
tracing::info!(
|
||||||
|
"Processing GET request for metric with ID: {}, user_id: {}",
|
||||||
|
id,
|
||||||
|
user.id
|
||||||
|
);
|
||||||
|
|
||||||
|
let metric = match get_metric_handler(&id, &user.id).await {
|
||||||
Ok(response) => response,
|
Ok(response) => response,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::error!("Error getting metric: {}", e);
|
tracing::error!("Error getting metric: {}", e);
|
||||||
|
|
|
@ -96,6 +96,7 @@ pub async fn get_user_information(user_id: &Uuid) -> Result<UserInfoObject> {
|
||||||
users::created_at,
|
users::created_at,
|
||||||
users::updated_at,
|
users::updated_at,
|
||||||
users::attributes,
|
users::attributes,
|
||||||
|
users::avatar_url,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
teams::id,
|
teams::id,
|
||||||
|
|
|
@ -101,6 +101,7 @@ async fn post_user_handler(
|
||||||
"user_email": email,
|
"user_email": email,
|
||||||
"organization_role": role.to_string(),
|
"organization_role": role.to_string(),
|
||||||
}),
|
}),
|
||||||
|
avatar_url: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let user_to_organization = UserToOrganization {
|
let user_to_organization = UserToOrganization {
|
||||||
|
|
|
@ -93,6 +93,7 @@ async fn invite_users_handler(user: &User, req: InviteUsersRequest) -> Result<()
|
||||||
"user_email": email,
|
"user_email": email,
|
||||||
"organization_role": "viewer".to_string(),
|
"organization_role": "viewer".to_string(),
|
||||||
}),
|
}),
|
||||||
|
avatar_url: None,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<User>>();
|
.collect::<Vec<User>>();
|
||||||
|
|
|
@ -383,6 +383,7 @@ pub async fn get_user_information(user_id: &Uuid) -> Result<UserInfoObject> {
|
||||||
users::created_at,
|
users::created_at,
|
||||||
users::updated_at,
|
users::updated_at,
|
||||||
users::attributes,
|
users::attributes,
|
||||||
|
users::avatar_url,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
teams::id,
|
teams::id,
|
||||||
|
|
|
@ -697,6 +697,7 @@ async fn create_new_users_and_add_permissions(
|
||||||
created_at: chrono::Utc::now(),
|
created_at: chrono::Utc::now(),
|
||||||
updated_at: chrono::Utc::now(),
|
updated_at: chrono::Utc::now(),
|
||||||
config: json!({}),
|
config: json!({}),
|
||||||
|
avatar_url: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let permission = AssetPermission {
|
let permission = AssetPermission {
|
||||||
|
|
Loading…
Reference in New Issue