diff --git a/database/migrations/0002_team.sql b/database/migrations/0002_team.sql index 70405345..ed4fd046 100644 --- a/database/migrations/0002_team.sql +++ b/database/migrations/0002_team.sql @@ -1,5 +1,6 @@ CREATE TABLE team( team_id TEXT NOT NULL UNIQUE, + grafana_id TEXT UNIQUE, team_name TEXT NOT NULL, personal BOOLEAN NOT NULL, subscription subscription, diff --git a/database/src/tables/team/select.rs b/database/src/tables/team/select.rs index 848ea4a3..42a7453c 100644 --- a/database/src/tables/team/select.rs +++ b/database/src/tables/team/select.rs @@ -11,7 +11,9 @@ impl Db { tx: Option<&mut Transaction<'_, sqlx::Postgres>>, team_id: &String, ) -> Result, DbError> { - let query = format!("SELECT * FROM {TEAM_TABLE_NAME} WHERE team_id = $1 AND deactivated_at IS NULL"); + let query = format!( + "SELECT * FROM {TEAM_TABLE_NAME} WHERE team_id = $1 AND deactivated_at IS NULL" + ); let typed_query = query_as::<_, Team>(&query); match tx { @@ -100,7 +102,9 @@ impl Db { } pub async fn get_team_by_admin_id(&self, admin_id: &String) -> Result, DbError> { - let query = format!("SELECT * FROM {TEAM_TABLE_NAME} WHERE team_admin_id = $1 AND deactivated_at IS NULL"); + let query = format!( + "SELECT * FROM {TEAM_TABLE_NAME} WHERE team_admin_id = $1 AND deactivated_at IS NULL" + ); let typed_query = query_as::<_, Team>(&query); return typed_query @@ -109,4 +113,13 @@ impl Db { .await .map_err(|e| e.into()); } + pub async fn get_all_teams(&self) -> Result, DbError> { + let query = format!("SELECT * FROM {TEAM_TABLE_NAME} WHERE deactivated_at IS NULL"); + let typed_query = query_as::<_, Team>(&query); + + return typed_query + .fetch_all(&self.connection_pool) + .await + .map_err(|e| e.into()); + } } diff --git a/database/src/tables/team/table_struct.rs b/database/src/tables/team/table_struct.rs index 51553e27..756d7acd 100644 --- a/database/src/tables/team/table_struct.rs +++ b/database/src/tables/team/table_struct.rs @@ -7,11 +7,12 @@ use sqlx::{ pub const TEAM_TABLE_NAME: &str = "team"; pub const TEAM_KEYS: &str = - "team_id, team_name, personal, subscription, team_admin_id, registration_timestamp, deactivated_at"; + "team_id, grafana_id, team_name, personal, subscription, team_admin_id, registration_timestamp, deactivated_at"; #[derive(Clone, Debug, Eq, PartialEq)] pub struct Team { pub team_id: String, + pub grafana_id: String, pub personal: bool, pub team_name: String, // Subscription is required to get access to the statistics @@ -25,6 +26,9 @@ impl FromRow<'_, PgRow> for Team { fn from_row(row: &sqlx::postgres::PgRow) -> std::result::Result { Ok(Team { team_id: row.get("team_id"), + grafana_id: row + .try_get::, _>("grafana_id")? + .unwrap_or_default(), team_name: row.get("team_name"), personal: row.get("personal"), subscription: row.get("subscription"), diff --git a/database/src/tables/team/update.rs b/database/src/tables/team/update.rs index a398db51..4f274cb2 100644 --- a/database/src/tables/team/update.rs +++ b/database/src/tables/team/update.rs @@ -16,11 +16,12 @@ impl Db { team: &Team, ) -> Result<(), DbError> { let query_body = format!( - "INSERT INTO {TEAM_TABLE_NAME} ({TEAM_KEYS}) VALUES ($1, $2, $3, $4, $5, $6, NULL)" + "INSERT INTO {TEAM_TABLE_NAME} ({TEAM_KEYS}) VALUES ($1, $2, $3, $4, $5, $6, $7, NULL)" ); let query_result = query(&query_body) .bind(&team.team_id) + .bind(&team.grafana_id) .bind(&team.team_name) .bind(&team.personal) .bind(&team.subscription) @@ -37,11 +38,12 @@ impl Db { pub async fn create_new_team(&self, team: &Team) -> Result<(), DbError> { let query_body = format!( - "INSERT INTO {TEAM_TABLE_NAME} ({TEAM_KEYS}) VALUES ($1, $2, $3, $4, $5, $6, NULL)" + "INSERT INTO {TEAM_TABLE_NAME} ({TEAM_KEYS}) VALUES ($1, $2, $3, $4, $5, $6, $7, NULL)" ); let query_result = query(&query_body) .bind(&team.team_id) + .bind(&team.grafana_id) .bind(&team.team_name) .bind(&team.personal) .bind(&team.subscription) @@ -96,6 +98,34 @@ impl Db { Err(e) => Err(e).map_err(|e| e.into()), } } + + pub async fn update_grafana_id(&self, team_id: &str, grafana_id: &str) -> Result<(), DbError> { + let query_body = format!( + "UPDATE {TEAM_TABLE_NAME} SET grafana_id = $1 WHERE team_id = $2 AND deactivated_at IS NULL", + ); + + let query_result = query(&query_body) + .bind(grafana_id) + .bind(team_id) + .execute(&self.connection_pool) + .await; + + match query_result { + Ok(_) => Ok(()), + Err(e) => Err(e).map_err(|e| e.into()), + } + } + + pub async fn clear_all_grafana_ids(&self) -> Result<(), DbError> { + let query_body = format!("UPDATE {TEAM_TABLE_NAME} SET grafana_id = NULL",); + + let query_result = query(&query_body).execute(&self.connection_pool).await; + + match query_result { + Ok(_) => Ok(()), + Err(e) => Err(e).map_err(|e| e.into()), + } + } } #[cfg(feature = "cloud_integration_tests")] @@ -121,6 +151,7 @@ mod tests { // Create team and register app let team = Team { team_id: "test_team_id".to_string(), + grafana_id: "test_grafana_id".to_string(), team_name: "test_team_name".to_string(), personal: false, subscription: None, diff --git a/database/src/tables/test_utils.rs b/database/src/tables/test_utils.rs index 571c5a25..7343415e 100644 --- a/database/src/tables/test_utils.rs +++ b/database/src/tables/test_utils.rs @@ -97,6 +97,7 @@ pub mod test_utils { let team = Team { team_id: team_id.clone(), + grafana_id: "test_grafana_id".to_string(), team_name: "test_team_name".to_string(), personal: false, subscription: None, diff --git a/database/src/tables/user_app_privileges/select.rs b/database/src/tables/user_app_privileges/select.rs index 9c27c82d..cd39f118 100644 --- a/database/src/tables/user_app_privileges/select.rs +++ b/database/src/tables/user_app_privileges/select.rs @@ -114,7 +114,7 @@ impl Db { // 17.10.2024 Hubert: "If this ever breaks, I will write comments." let query = format!( "WITH RelevantTeams AS ( - SELECT DISTINCT t.team_id, t.team_name, t.personal, t.subscription, + SELECT DISTINCT t.team_id, t.grafana_id, t.team_name, t.personal, t.subscription, t.registration_timestamp, gu.email AS team_admin_email, gu.user_id AS team_admin_id, t.deactivated_at, CASE @@ -127,7 +127,7 @@ impl Db { JOIN {USERS_TABLE_NAME} gu ON t.team_admin_id = gu.user_id WHERE (t.team_admin_id = $1 OR uap.user_id = $1) AND t.deactivated_at IS NULL ) - SELECT rt.team_id, rt.team_name, rt.personal, rt.subscription, rt.registration_timestamp, + SELECT rt.team_id, rt.grafana_id, rt.team_name, rt.personal, rt.subscription, rt.registration_timestamp, rt.team_admin_email, rt.team_admin_id, ra.app_id, ra.app_name, ra.whitelisted_domains, ra.ack_public_keys, ra.registration_timestamp AS app_registration_timestamp, uap.user_id, uap.privilege_level, uap.creation_timestamp AS privilege_creation_timestamp, @@ -163,6 +163,7 @@ impl Db { for row in rows { let team = Team { team_id: row.get("team_id"), + grafana_id: row.get("grafana_id"), personal: row.get("personal"), team_name: row.get("team_name"), subscription: row.get("subscription"), diff --git a/database/src/tables/user_app_privileges/update.rs b/database/src/tables/user_app_privileges/update.rs index f647e7bd..700fa216 100644 --- a/database/src/tables/user_app_privileges/update.rs +++ b/database/src/tables/user_app_privileges/update.rs @@ -371,6 +371,7 @@ mod tests { let team = Team { team_id: team_id.clone(), + grafana_id: "test_grafana_id_2".to_string(), team_name: "test_team_name".to_string(), personal: false, subscription: None, diff --git a/server/src/bin/grafana_restore.rs b/server/src/bin/grafana_restore.rs new file mode 100644 index 00000000..4855f7b4 --- /dev/null +++ b/server/src/bin/grafana_restore.rs @@ -0,0 +1,114 @@ +use core::panic; +use database::db::Db; +use openapi::apis::configuration::Configuration; +use server::env::{GF_SECURITY_ADMIN_PASSWORD, GF_SECURITY_ADMIN_USER, GRAFANA_BASE_PATH}; +use server::http::cloud::grafana_utils::add_user_to_team::handle_grafana_add_user_to_team; +use server::http::cloud::grafana_utils::create_new_app::handle_grafana_create_new_app; +use server::http::cloud::grafana_utils::create_new_team::handle_grafana_create_new_team; +use server::utils::import_template_dashboards; +use std::collections::HashSet; +use std::sync::Arc; + +// This script is used to restore the state of the Grafana instance +// Before running this script, clear the contents of the grafana-data folder +#[tokio::main] +async fn main() { + let db = Db::connect_to_the_pool().await; + let mut conf: Configuration = Configuration::new(); + conf.base_path = GRAFANA_BASE_PATH().to_string(); + conf.basic_auth = Some(( + GF_SECURITY_ADMIN_USER().to_string(), + Some(GF_SECURITY_ADMIN_PASSWORD().to_string()), + )); + + let grafana_client_conf = Arc::new(conf); + // Setup template dashboards + import_template_dashboards(&grafana_client_conf).await; + + if let Err(err) = db.clear_all_grafana_ids().await { + panic!("Failed to clear grafana ids in database: {:?}", err); + } + + let teams = match db.get_all_teams().await { + Ok(teams) => teams, + Err(e) => { + panic!("Failed to get teams. Error: {:?}", e); + } + }; + for team in teams { + // create teams + let grafana_id = match handle_grafana_create_new_team( + &grafana_client_conf, + &team.team_admin_id, + &team.team_name, + ) + .await + { + Ok(id) => { + if let Err(err) = db.update_grafana_id(&team.team_id, &id.to_string()).await { + panic!("Failed to update grafana id in database: {:?}", err); + } + id.to_string() + } + Err(err) => { + panic!("Failed to create team in grafana: {:?}", err); + } + }; + + let privileges = match db.get_privileges_by_team_id(&team.team_id).await { + Ok(privileges) => privileges, + Err(e) => { + panic!("Failed to get privileges. Error: {:?}", e); + } + }; + let unique_user_ids: Vec = privileges + .into_iter() + .map(|privilege| privilege.user_id) + .collect::>() + .into_iter() + .collect(); + + let users_emails = match db.get_users_emails_by_ids(&unique_user_ids).await { + Ok(emails) => emails, + Err(e) => { + panic!("Failed to get users emails. Error: {:?}", e); + } + }; + for (_, user_email) in users_emails { + // add users to teams + match handle_grafana_add_user_to_team(&grafana_client_conf, &grafana_id, &user_email) + .await + { + Ok(id) => id, + Err(err) => { + panic!("Failed to add user to team in grafana: {:?}", err); + } + }; + } + let apps = match db.get_registered_apps_by_team_id(&team.team_id).await { + Ok(apps) => apps, + Err(e) => { + panic!("Failed to get apps. Error: {:?}", e); + } + }; + for app in apps { + let app_id = app.app_id.clone(); + let app_name = app.app_name.clone(); + // create apps + match handle_grafana_create_new_app( + &grafana_client_conf, + &app_id, + &app_name, + &grafana_id, + ) + .await + { + Ok(id) => id, + Err(err) => { + panic!("Failed to create app in grafana: {:?}", err); + } + }; + } + } + println!("Got it! Exiting..."); +} diff --git a/server/src/http/cloud/accept_team_invite.rs b/server/src/http/cloud/accept_team_invite.rs index c34818c4..8b475403 100644 --- a/server/src/http/cloud/accept_team_invite.rs +++ b/server/src/http/cloud/accept_team_invite.rs @@ -1,6 +1,6 @@ use super::{ grafana_utils::add_user_to_team::handle_grafana_add_user_to_team, - utils::{custom_validate_team_id, validate_request}, + utils::{custom_validate_uuid, validate_request}, }; use crate::{ env::is_env_production, middlewares::auth_middleware::UserId, @@ -19,7 +19,7 @@ use ts_rs::TS; #[ts(export)] #[serde(rename_all = "camelCase")] pub struct HttpAcceptTeamInviteRequest { - #[garde(custom(custom_validate_team_id))] + #[garde(custom(custom_validate_uuid))] pub team_id: String, } @@ -101,10 +101,26 @@ pub async fn accept_team_invite( } } + let grafana_team_id = match db.get_team_by_team_id(None, &request.team_id).await { + Ok(Some(team)) => team.grafana_id, + Ok(None) => { + return Err(( + StatusCode::BAD_REQUEST, + CloudApiErrors::TeamDoesNotExist.to_string(), + )); + } + Err(err) => { + error!("Failed to get team by team_id: {:?}", err); + return Err(( + StatusCode::INTERNAL_SERVER_ERROR, + CloudApiErrors::DatabaseError.to_string(), + )); + } + }; // Grafana add user to the team if is_env_production() { if let Err(err) = - handle_grafana_add_user_to_team(&grafana_conf, &request.team_id, &user.email).await + handle_grafana_add_user_to_team(&grafana_conf, &grafana_team_id, &user.email).await { error!("Failed to add user to the team in grafana: {:?}", err); return Err(( diff --git a/server/src/http/cloud/cancel_team_user_invite.rs b/server/src/http/cloud/cancel_team_user_invite.rs index 1bb670ef..3a416166 100644 --- a/server/src/http/cloud/cancel_team_user_invite.rs +++ b/server/src/http/cloud/cancel_team_user_invite.rs @@ -1,4 +1,4 @@ -use super::utils::{custom_validate_team_id, validate_request}; +use super::utils::{custom_validate_uuid, validate_request}; use crate::{ middlewares::auth_middleware::UserId, structs::cloud::{api_cloud_errors::CloudApiErrors, team_invite::TeamInvite}, @@ -15,7 +15,7 @@ use ts_rs::TS; #[ts(export)] #[serde(rename_all = "camelCase")] pub struct HttpCancelTeamUserInviteRequest { - #[garde(custom(custom_validate_team_id))] + #[garde(custom(custom_validate_uuid))] pub team_id: String, #[garde(email)] pub user_email: String, diff --git a/server/src/http/cloud/cancel_user_team_invite.rs b/server/src/http/cloud/cancel_user_team_invite.rs index cb4521e6..1554c3a1 100644 --- a/server/src/http/cloud/cancel_user_team_invite.rs +++ b/server/src/http/cloud/cancel_user_team_invite.rs @@ -1,4 +1,4 @@ -use super::utils::{custom_validate_team_id, validate_request}; +use super::utils::{custom_validate_uuid, validate_request}; use crate::{ middlewares::auth_middleware::UserId, structs::cloud::api_cloud_errors::CloudApiErrors, }; @@ -14,7 +14,7 @@ use ts_rs::TS; #[ts(export)] #[serde(rename_all = "camelCase")] pub struct HttpCancelUserTeamInviteRequest { - #[garde(custom(custom_validate_team_id))] + #[garde(custom(custom_validate_uuid))] pub team_id: String, } diff --git a/server/src/http/cloud/change_user_privileges.rs b/server/src/http/cloud/change_user_privileges.rs index 2abe1325..a5e34d9d 100644 --- a/server/src/http/cloud/change_user_privileges.rs +++ b/server/src/http/cloud/change_user_privileges.rs @@ -1,4 +1,4 @@ -use super::utils::{custom_validate_team_id, custom_validate_uuid, validate_request}; +use super::utils::{custom_validate_uuid, validate_request}; use crate::{ middlewares::auth_middleware::UserId, structs::cloud::{ @@ -36,7 +36,7 @@ pub struct PrivilegeChange { #[ts(export)] #[serde(rename_all = "camelCase")] pub struct HttpChangeUsersPrivilegesRequest { - #[garde(custom(custom_validate_team_id))] + #[garde(custom(custom_validate_uuid))] pub team_id: String, #[garde(dive)] pub privileges_changes: Vec, diff --git a/server/src/http/cloud/delete_team.rs b/server/src/http/cloud/delete_team.rs index 4975d90c..44a68fd0 100644 --- a/server/src/http/cloud/delete_team.rs +++ b/server/src/http/cloud/delete_team.rs @@ -1,4 +1,4 @@ -use super::utils::{custom_validate_team_id, validate_request}; +use super::utils::{custom_validate_uuid, validate_request}; use crate::{ env::is_env_production, http::cloud::grafana_utils::delete_team::handle_grafana_delete_team, @@ -18,7 +18,7 @@ use ts_rs::TS; #[ts(export)] #[serde(rename_all = "camelCase")] pub struct HttpDeleteTeamRequest { - #[garde(custom(custom_validate_team_id))] + #[garde(custom(custom_validate_uuid))] pub team_id: String, } @@ -153,7 +153,7 @@ pub async fn delete_team( // Grafana, delete team // TODO, fix this by fixing methods for setting up grafana datasource if is_env_production() { - if let Err(err) = handle_grafana_delete_team(&grafana_conf, &request.team_id).await { + if let Err(err) = handle_grafana_delete_team(&grafana_conf, &team.grafana_id).await { error!("Failed to delete team from grafana: {:?}", err); return Err(( StatusCode::INTERNAL_SERVER_ERROR, diff --git a/server/src/http/cloud/get_team_metadata.rs b/server/src/http/cloud/get_team_metadata.rs index 77dcf006..4196e84e 100644 --- a/server/src/http/cloud/get_team_metadata.rs +++ b/server/src/http/cloud/get_team_metadata.rs @@ -1,4 +1,4 @@ -use super::utils::custom_validate_team_id; +use super::utils::custom_validate_uuid; use crate::{ middlewares::auth_middleware::UserId, structs::cloud::{ @@ -24,7 +24,7 @@ use ts_rs::TS; #[ts(export)] #[serde(rename_all = "camelCase")] pub struct HttpGetTeamMetadataRequest { - #[garde(custom(custom_validate_team_id))] + #[garde(custom(custom_validate_uuid))] pub team_id: String, } diff --git a/server/src/http/cloud/get_team_user_invites.rs b/server/src/http/cloud/get_team_user_invites.rs index 0ebed4d1..3152e7f4 100644 --- a/server/src/http/cloud/get_team_user_invites.rs +++ b/server/src/http/cloud/get_team_user_invites.rs @@ -1,4 +1,4 @@ -use super::utils::{custom_validate_team_id, validate_request}; +use super::utils::{custom_validate_uuid, validate_request}; use crate::{ middlewares::auth_middleware::UserId, structs::cloud::{api_cloud_errors::CloudApiErrors, team_invite::TeamInvite}, @@ -19,7 +19,7 @@ use ts_rs::TS; #[ts(export)] #[serde(rename_all = "camelCase")] pub struct HttpGetTeamUserInvitesRequest { - #[garde(custom(custom_validate_team_id))] + #[garde(custom(custom_validate_uuid))] pub team_id: String, } diff --git a/server/src/http/cloud/get_team_users_privileges.rs b/server/src/http/cloud/get_team_users_privileges.rs index 4576ee98..d87c77c4 100644 --- a/server/src/http/cloud/get_team_users_privileges.rs +++ b/server/src/http/cloud/get_team_users_privileges.rs @@ -1,4 +1,4 @@ -use super::utils::custom_validate_team_id; +use super::utils::custom_validate_uuid; use crate::{ middlewares::auth_middleware::UserId, structs::cloud::{api_cloud_errors::CloudApiErrors, team_user_privilege::TeamUserPrivilege}, @@ -19,7 +19,7 @@ use ts_rs::TS; #[ts(export)] #[serde(rename_all = "camelCase")] pub struct HttpGetTeamUsersPrivilegesRequest { - #[garde(custom(custom_validate_team_id))] + #[garde(custom(custom_validate_uuid))] pub team_id: String, } diff --git a/server/src/http/cloud/get_user_joined_teams.rs b/server/src/http/cloud/get_user_joined_teams.rs index 8b3b8630..299ae389 100644 --- a/server/src/http/cloud/get_user_joined_teams.rs +++ b/server/src/http/cloud/get_user_joined_teams.rs @@ -76,6 +76,7 @@ pub async fn get_user_joined_teams( // Parse joined team let joined_team = JoinedTeam { team_id: team.team_id.clone(), + grafana_id: team.grafana_id, team_name: team.team_name, created_at: team.registration_timestamp, creator_email: admin_email, diff --git a/server/src/http/cloud/invite_user_to_team.rs b/server/src/http/cloud/invite_user_to_team.rs index 0642c4ba..7c7c0019 100644 --- a/server/src/http/cloud/invite_user_to_team.rs +++ b/server/src/http/cloud/invite_user_to_team.rs @@ -1,4 +1,4 @@ -use super::utils::{custom_validate_team_id, validate_request}; +use super::utils::{custom_validate_uuid, validate_request}; use crate::{ mailer::{ mail_requests::{SendEmailRequest, TeamInviteNotification}, @@ -21,7 +21,7 @@ use ts_rs::TS; #[ts(export)] #[serde(rename_all = "camelCase")] pub struct HttpInviteUserToTeamRequest { - #[garde(custom(custom_validate_team_id))] + #[garde(custom(custom_validate_uuid))] pub team_id: String, #[garde(email)] pub user_email: String, diff --git a/server/src/http/cloud/leave_team.rs b/server/src/http/cloud/leave_team.rs index 239bdab5..7f9c7d5f 100644 --- a/server/src/http/cloud/leave_team.rs +++ b/server/src/http/cloud/leave_team.rs @@ -1,6 +1,6 @@ use super::{ grafana_utils::remove_user_from_the_team::handle_grafana_remove_user_from_team, - utils::{custom_validate_team_id, validate_request}, + utils::{custom_validate_uuid, validate_request}, }; use crate::{ env::is_env_production, @@ -24,7 +24,7 @@ use ts_rs::TS; #[ts(export)] #[serde(rename_all = "camelCase")] pub struct HttpLeaveTeamRequest { - #[garde(custom(custom_validate_team_id))] + #[garde(custom(custom_validate_uuid))] pub team_id: String, #[garde(alphanumeric)] pub device: String, @@ -108,7 +108,7 @@ pub async fn leave_team( if is_env_production() { if let Err(err) = handle_grafana_remove_user_from_team( &grafana_conf, - &request.team_id, + &team.grafana_id, &user.email, ) .await diff --git a/server/src/http/cloud/register_new_app.rs b/server/src/http/cloud/register_new_app.rs index 458dfd1d..858926fd 100644 --- a/server/src/http/cloud/register_new_app.rs +++ b/server/src/http/cloud/register_new_app.rs @@ -1,6 +1,6 @@ use super::{ grafana_utils::create_new_app::handle_grafana_create_new_app, - utils::{custom_validate_name, custom_validate_team_id, validate_request}, + utils::{custom_validate_name, custom_validate_uuid, validate_request}, }; use crate::{ env::is_env_production, middlewares::auth_middleware::UserId, @@ -23,7 +23,7 @@ use uuid7::uuid7; #[ts(export)] #[serde(rename_all = "camelCase")] pub struct HttpRegisterNewAppRequest { - #[garde(custom(custom_validate_team_id))] + #[garde(custom(custom_validate_uuid))] pub team_id: String, #[garde(custom(custom_validate_name))] pub app_name: String, @@ -116,7 +116,7 @@ pub async fn register_new_app( &grafana_conf, &request.app_name, &app_id, - &team.team_id, + &team.grafana_id, ) .await { diff --git a/server/src/http/cloud/register_new_team.rs b/server/src/http/cloud/register_new_team.rs index 0b10cdd6..d2e460c2 100644 --- a/server/src/http/cloud/register_new_team.rs +++ b/server/src/http/cloud/register_new_team.rs @@ -17,6 +17,7 @@ use openapi::apis::configuration::Configuration; use serde::{Deserialize, Serialize}; use std::sync::Arc; use ts_rs::TS; +use uuid7::uuid7; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, TS, Validate)] #[ts(export)] @@ -141,10 +142,13 @@ pub async fn register_new_team( } } } + // Generate a new app id + let team_id = uuid7().to_string(); // Create a new team let team = Team { - team_id: grafana_team_id.to_string(), + team_id: team_id.to_string(), + grafana_id: grafana_team_id.to_string(), team_name: request.team_name.clone(), team_admin_id: user_id.clone(), subscription: None, @@ -162,7 +166,7 @@ pub async fn register_new_team( } return Ok(Json(HttpRegisterNewTeamResponse { - team_id: grafana_team_id.to_string(), + team_id: team_id.to_string(), })); } Err(err) => { diff --git a/server/src/http/cloud/remove_user_from_team.rs b/server/src/http/cloud/remove_user_from_team.rs index bdc11d06..51ba31d7 100644 --- a/server/src/http/cloud/remove_user_from_team.rs +++ b/server/src/http/cloud/remove_user_from_team.rs @@ -1,6 +1,6 @@ use super::{ grafana_utils::remove_user_from_the_team::handle_grafana_remove_user_from_team, - utils::{custom_validate_team_id, validate_request}, + utils::{custom_validate_uuid, validate_request}, }; use crate::{ env::is_env_production, @@ -24,7 +24,7 @@ use ts_rs::TS; #[ts(export)] #[serde(rename_all = "camelCase")] pub struct HttpRemoveUserFromTeamRequest { - #[garde(custom(custom_validate_team_id))] + #[garde(custom(custom_validate_uuid))] pub team_id: String, #[garde(email)] pub user_email: String, @@ -110,7 +110,7 @@ pub async fn remove_user_from_team( if is_env_production() { if let Err(err) = handle_grafana_remove_user_from_team( &grafana_conf, - &request.team_id, + &team.grafana_id, &request.user_email, ) .await diff --git a/server/src/http/cloud/utils.rs b/server/src/http/cloud/utils.rs index a806269a..04c0d6b7 100644 --- a/server/src/http/cloud/utils.rs +++ b/server/src/http/cloud/utils.rs @@ -49,19 +49,6 @@ pub fn custom_validate_uuid(string_uuid: &String, _context: &()) -> garde::Resul Ok(()) } -pub fn custom_validate_team_id(string_id: &String, _context: &()) -> garde::Result { - // For now we are using i64 returned from grafana as team_id, hopefully this will be changed to UUID - match i64::from_str(string_id) { - Ok(id) => { - if id < 0 { - return Err(garde::Error::new("Invalid ID format".to_string())); - } - } - Err(_) => return Err(garde::Error::new("Invalid ID format".to_string())), - } - Ok(()) -} - pub fn custom_validate_name(name: &String, _context: &()) -> garde::Result { NAME_REGEX .is_match(name) diff --git a/server/src/structs/cloud/joined_team.rs b/server/src/structs/cloud/joined_team.rs index 3b4117c7..c81ecc49 100644 --- a/server/src/structs/cloud/joined_team.rs +++ b/server/src/structs/cloud/joined_team.rs @@ -9,6 +9,7 @@ pub type TeamId = String; #[serde(rename_all = "camelCase")] pub struct JoinedTeam { pub team_id: TeamId, + pub grafana_id: String, pub team_name: String, pub creator_email: String, pub created_at: DateTime,