Skip to main content
Glama
history_event.rs3.35 kB
use chrono::{ DateTime, Utc, }; use serde::{ Deserialize, Serialize, }; use si_events::Timestamp; use si_id::UserPk; use crate::{ Result, actor_view::ActorView, context::SiDbContext, tenancy::Tenancy, transactions::SiDbTransactions as _, user::User, }; const SYSTEMINIT_EMAIL_SUFFIX: &str = "@systeminit.com"; const TEST_SYSTEMINIT_EMAIL_SUFFIX: &str = "@test.systeminit.com"; #[remain::sorted] #[derive(Deserialize, Serialize, Debug, PartialEq, Eq, strum::Display, Clone, Copy, Hash)] pub enum HistoryActor { SystemInit, User(UserPk), } impl HistoryActor { pub fn distinct_id(&self) -> String { match self { HistoryActor::User(pk) => pk.to_string(), HistoryActor::SystemInit => "unknown-backend".to_string(), } } pub async fn email(&self, ctx: &impl SiDbContext) -> Result<String> { Ok(match self { HistoryActor::SystemInit => "sally@systeminit.com".to_string(), HistoryActor::User(user_pk) => User::get_by_pk(ctx, *user_pk).await?.email().clone(), }) } pub async fn email_is_systeminit(&self, ctx: &impl SiDbContext) -> Result<bool> { let email_as_lowercase = self.email(ctx).await?.to_lowercase(); Ok(email_as_lowercase.ends_with(SYSTEMINIT_EMAIL_SUFFIX) || email_as_lowercase.ends_with(TEST_SYSTEMINIT_EMAIL_SUFFIX)) } pub fn user_pk(&self) -> Option<UserPk> { match self { HistoryActor::User(pk) => Some(*pk), HistoryActor::SystemInit => None, } } } impl From<UserPk> for HistoryActor { fn from(pk: UserPk) -> Self { HistoryActor::User(pk) } } pub use si_id::HistoryEventPk; /// HistoryEvents are the audit trail for things in SI. They track /// that a specific actor did something, and optionally store data /// associated with the activity for posterity. #[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] pub struct HistoryEvent { pub pk: HistoryEventPk, pub label: String, pub actor: HistoryActor, pub message: String, pub data: serde_json::Value, #[serde(flatten)] pub tenancy: Tenancy, #[serde(flatten)] pub timestamp: Timestamp, } impl HistoryEvent { pub async fn new( ctx: &impl SiDbContext, label: impl AsRef<str>, message: impl AsRef<str>, data: &serde_json::Value, ) -> Result<HistoryEvent> { let label = label.as_ref(); let message = message.as_ref(); let actor = serde_json::to_value(ctx.history_actor())?; let txns = ctx.txns().await?; let row = txns .pg() .query_one( "SELECT object FROM history_event_create_v1($1, $2, $3, $4, $5)", &[&label.to_string(), &actor, &message, &data, ctx.tenancy()], ) .await?; let json: serde_json::Value = row.try_get("object")?; // TODO(fnichol): determine subject(s) for publishing txns.nats().publish("historyEvent", &json).await?; let object: HistoryEvent = serde_json::from_value(json)?; Ok(object) } } #[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct HistoryEventMetadata { pub actor: ActorView, pub timestamp: DateTime<Utc>, }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/systeminit/si'

If you have feedback or need assistance with the MCP directory API, please join our Discord server