use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use strum::{Display, EnumString};
use uuid::Uuid;
pub const PROVIDER_ACCOUNT_SECRET_PREFIX: &str = "accounts/";
pub fn provider_account_secret_key(id: Uuid) -> String {
format!("{PROVIDER_ACCOUNT_SECRET_PREFIX}{id}/credential")
}
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "store-postgres", derive(sqlx::Type))]
#[cfg_attr(
feature = "store-postgres",
sqlx(type_name = "text", rename_all = "snake_case")
)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Display, EnumString)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum AccountWindowStatus {
Allowed,
AllowedWarning,
Rejected,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ProviderAccount {
pub id: Uuid,
pub name: String,
pub display_name: String,
pub kind: String,
pub secret_key: String,
pub enabled: bool,
pub priority: i32,
pub tags: Vec<String>,
pub max_concurrency: Option<u32>,
pub alert_threshold: f64,
pub expires_at: DateTime<Utc>,
pub plan: Option<String>,
pub auth_failed_at: Option<DateTime<Utc>>,
pub created_by: Option<Uuid>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NewProviderAccount {
pub id: Uuid,
pub name: String,
pub display_name: String,
pub kind: String,
pub secret_key: String,
pub enabled: bool,
pub priority: i32,
pub tags: Vec<String>,
pub max_concurrency: Option<u32>,
pub alert_threshold: f64,
pub expires_at: DateTime<Utc>,
pub plan: Option<String>,
pub created_by: Option<Uuid>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct ProviderAccountUpdate {
pub display_name: Option<String>,
pub enabled: Option<bool>,
pub priority: Option<i32>,
pub tags: Option<Vec<String>>,
pub max_concurrency: Option<Option<u32>>,
pub alert_threshold: Option<f64>,
pub expires_at: Option<DateTime<Utc>>,
pub plan: Option<Option<String>>,
pub auth_failed_at: Option<Option<DateTime<Utc>>>,
}
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ProviderAccountWindow {
pub account_id: Uuid,
pub window: String,
pub utilization: f64,
pub resets_at: Option<DateTime<Utc>>,
pub status: AccountWindowStatus,
pub model_scope: Option<String>,
pub observed_at: DateTime<Utc>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ProviderAccountUsagePoint {
pub id: Uuid,
pub account_id: Uuid,
pub window: String,
pub utilization: f64,
pub resets_at: Option<DateTime<Utc>>,
pub status: AccountWindowStatus,
pub model_scope: Option<String>,
pub observed_at: DateTime<Utc>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct NewAccountWindow {
pub window: String,
pub utilization: f64,
pub resets_at: Option<DateTime<Utc>>,
pub status: AccountWindowStatus,
pub model_scope: Option<String>,
pub observed_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct NewProviderAccountObservation {
pub windows: Vec<NewAccountWindow>,
#[serde(default)]
pub auth_failed: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ProviderAccountCandidate {
pub account: ProviderAccount,
pub windows: Vec<ProviderAccountWindow>,
pub running_steps: u32,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn secret_key_is_under_the_accounts_prefix() {
let id = Uuid::now_v7();
let key = provider_account_secret_key(id);
assert!(key.starts_with(PROVIDER_ACCOUNT_SECRET_PREFIX));
assert!(key.ends_with("/credential"));
assert!(key.contains(&id.to_string()));
}
#[test]
fn window_status_wire_format() {
assert_eq!(AccountWindowStatus::Rejected.to_string(), "rejected");
assert_eq!(
serde_json::to_string(&AccountWindowStatus::AllowedWarning).unwrap(),
"\"allowed_warning\""
);
assert!("nope".parse::<AccountWindowStatus>().is_err());
}
#[test]
fn observation_auth_failed_defaults_to_false() {
let observation: NewProviderAccountObservation =
serde_json::from_str(r#"{"windows":[]}"#).unwrap();
assert!(!observation.auth_failed);
}
}