use crate::contracts::descriptor::{
ContractDescriptor, EncodedPayload, Interaction, PayloadDescriptor, Version,
};
use crate::contracts::envelope::MessageEnvelope;
use crate::contracts::metadata::{ContractMetadata, Participants};
use crate::contracts::{UniversalEvent, UniversalEventError};
use crate::events::EventName;
use crate::identity::{
CapabilityId, ContractId, CorrelationId, EngineId, EventId, MessageId, OperationId,
};
use crate::operation::OperationContext;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ObservabilityEvent {
event: UniversalEvent,
}
impl ObservabilityEvent {
pub fn new(
event_id: EventId,
event_name: EventName,
event_type: impl Into<String>,
scope: impl Into<String>,
operation_context: OperationContext,
) -> Result<Self, UniversalEventError> {
let event_type = event_type.into();
let scope = scope.into();
let message_id = MessageId::new(format!("observability-message-{event_id}"))
.expect("derived observability message ids are non-empty");
let descriptor = ContractDescriptor::new(
ContractId::new("observability.event")
.expect("static observability contract id is valid"),
CapabilityId::new("observability.emit")
.expect("static observability capability id is valid"),
Version::new(1, 0, 0),
Interaction::Event,
PayloadDescriptor::new("application/octet-stream", Version::new(1, 0, 0))
.expect("static observability payload descriptor is valid"),
);
let payload_descriptor = descriptor.payload.clone();
let metadata = ContractMetadata::new(
descriptor,
Participants::new(
EngineId::new("observability").expect("static observability engine id is valid"),
EngineId::new("observability-system")
.expect("static observability-system engine id is valid"),
),
);
let envelope = MessageEnvelope::new(
message_id,
operation_context,
metadata,
EncodedPayload::new(payload_descriptor, vec![0]),
);
let event = UniversalEvent::from_parts(
envelope,
event_id,
event_name.to_string(),
event_type,
scope,
)?;
Ok(Self { event })
}
pub fn universal_event(&self) -> &UniversalEvent {
&self.event
}
pub fn event_id(&self) -> &EventId {
self.event.event_id()
}
pub fn message_id(&self) -> &MessageId {
self.event.message_id()
}
pub fn event_name(&self) -> &EventName {
self.event.event_name()
}
pub fn event_type(&self) -> &str {
self.event.event_type()
}
pub fn event_scope(&self) -> &str {
self.event.scope()
}
pub fn operation_context(&self) -> &OperationContext {
&self.event.envelope.operation_context
}
pub fn operation_id(&self) -> &OperationId {
&self.operation_context().operation.id
}
pub fn correlation_id(&self) -> &CorrelationId {
&self.operation_context().operation.correlation_id
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::identity::{CorrelationId, OperationId};
use crate::operation::{Operation, OperationContext};
fn operation_context() -> OperationContext {
OperationContext::new(Operation::new(
OperationId::new("operation-1").unwrap(),
CorrelationId::new("correlation-1").unwrap(),
))
}
fn event(event_id: &str) -> ObservabilityEvent {
ObservabilityEvent::new(
EventId::new(event_id).unwrap(),
EventName::new("observability.event").unwrap(),
"observability",
"global",
operation_context(),
)
.unwrap()
}
#[test]
fn constructs_the_universal_event_internally() {
let event = event("event-1");
assert!(!event.event_id().as_str().is_empty());
assert_eq!(event.event_id().as_str(), "event-1");
assert_eq!(event.message_id().as_str(), "observability-message-event-1");
assert_eq!(event.event_name().as_str(), "observability.event");
assert_eq!(event.event_type(), "observability");
assert_eq!(event.event_scope(), "global");
}
#[test]
fn exposes_operation_and_correlation_identity() {
let event = event("event-2");
assert_eq!(event.operation_id().as_str(), "operation-1");
assert_eq!(event.correlation_id().as_str(), "correlation-1");
}
#[test]
fn exposes_the_complete_universal_event() {
let event = event("event-3");
assert_eq!(event.universal_event().event_id(), event.event_id());
assert_eq!(event.universal_event().event_name(), event.event_name());
assert_eq!(event.universal_event().event_type(), event.event_type());
assert_eq!(event.universal_event().scope(), event.event_scope());
}
#[test]
fn rejects_empty_event_type_without_panicking() {
let result = ObservabilityEvent::new(
EventId::new("event-empty-type").unwrap(),
EventName::new("diagnostic.created").unwrap(),
" ",
"global",
operation_context(),
);
assert_eq!(result, Err(UniversalEventError::EmptyEventType));
}
#[test]
fn rejects_empty_event_scope_without_panicking() {
let result = ObservabilityEvent::new(
EventId::new("event-empty-scope").unwrap(),
EventName::new("diagnostic.created").unwrap(),
"diagnostic",
" \t",
operation_context(),
);
assert_eq!(result, Err(UniversalEventError::EmptyEventScope));
}
#[test]
fn preserves_domain_inputs_in_the_universal_event() {
let event = ObservabilityEvent::new(
EventId::new("event-4").unwrap(),
EventName::new("diagnostic.created").unwrap(),
"diagnostic",
"global",
operation_context(),
)
.unwrap();
assert_eq!(event.event_name().as_str(), "diagnostic.created");
assert_eq!(event.event_type(), "diagnostic");
assert_eq!(event.event_scope(), "global");
}
}