// file: kb_lib/src/db/dtos/onchain_observation.rs //! On-chain observation DTO. /// Application-facing on-chain observation DTO. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct KbOnchainObservationDto { /// Optional numeric primary key. pub id: std::option::Option, /// Observation kind. pub observation_kind: std::string::String, /// Observation source family. pub source_kind: crate::KbObservationSourceKind, /// Optional source endpoint logical name. pub endpoint_name: std::option::Option, /// Logical object key, for example a mint, signature or pool address. pub object_key: std::string::String, /// Optional slot number. pub slot: std::option::Option, /// Raw JSON payload. pub payload: serde_json::Value, /// Observation timestamp. pub observed_at: chrono::DateTime, } impl KbOnchainObservationDto { /// Creates a new on-chain observation DTO with the current timestamp. pub fn new( observation_kind: std::string::String, source_kind: crate::KbObservationSourceKind, endpoint_name: std::option::Option, object_key: std::string::String, slot: std::option::Option, payload: serde_json::Value, ) -> Self { Self { id: None, observation_kind, source_kind, endpoint_name, object_key, slot, payload, observed_at: chrono::Utc::now(), } } } impl TryFrom for KbOnchainObservationDto { type Error = crate::KbError; fn try_from(entity: crate::KbOnchainObservationEntity) -> Result { let source_kind_result = crate::KbObservationSourceKind::from_i16(entity.source_kind); let source_kind = match source_kind_result { Ok(source_kind) => source_kind, Err(error) => return Err(error), }; let observed_at_result = chrono::DateTime::parse_from_rfc3339(&entity.observed_at); let observed_at = match observed_at_result { Ok(observed_at) => observed_at.with_timezone(&chrono::Utc), Err(error) => { return Err(crate::KbError::Db(format!( "cannot parse on-chain observation observed_at '{}': {}", entity.observed_at, error ))); } }; let payload_result = serde_json::from_str::(&entity.payload_json); let payload = match payload_result { Ok(payload) => payload, Err(error) => { return Err(crate::KbError::Db(format!( "cannot parse on-chain observation payload_json '{}': {}", entity.payload_json, error ))); } }; let slot = match entity.slot { Some(slot) => { let slot_result = u64::try_from(slot); match slot_result { Ok(slot) => Some(slot), Err(error) => { return Err(crate::KbError::Db(format!( "cannot convert on-chain observation slot '{}' to u64: {}", slot, error ))); } } } None => None, }; Ok(Self { id: Some(entity.id), observation_kind: entity.observation_kind, source_kind, endpoint_name: entity.endpoint_name, object_key: entity.object_key, slot, payload, observed_at, }) } }