use serde::Serialize;
use super::registry::EndpointRegistry;
use super::scheduler::{ComputationGraphDeclaration, ComputationGraphScheduler};
pub struct EmbeddedGraph {
scheduler: ComputationGraphScheduler,
registry: EndpointRegistry,
graph_name: String,
}
impl EmbeddedGraph {
pub async fn spawn(decl: ComputationGraphDeclaration) -> Result<Self, String> {
let registry = EndpointRegistry::new();
let scheduler = ComputationGraphScheduler::new(registry.clone());
let graph_name = decl.name.clone();
scheduler.load_graph(decl).await?;
Ok(Self {
scheduler,
registry,
graph_name,
})
}
pub async fn push(&self, accumulator: &str, event: &impl Serialize) -> Result<(), String> {
let bytes = serde_json::to_vec(event).map_err(|e| e.to_string())?;
self.push_raw(accumulator, bytes).await
}
pub async fn push_raw(&self, accumulator: &str, bytes: Vec<u8>) -> Result<(), String> {
self.registry
.send_to_accumulator(accumulator, bytes)
.await
.map(|_| ())
.map_err(|e| e.to_string())
}
pub fn graph_name(&self) -> &str {
&self.graph_name
}
pub fn scheduler(&self) -> &ComputationGraphScheduler {
&self.scheduler
}
pub fn registry(&self) -> &EndpointRegistry {
&self.registry
}
pub async fn shutdown(self) {
self.scheduler.shutdown_all().await;
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::computation_graph::packaging_bridge::PassthroughAccumulatorFactory;
use crate::computation_graph::reactor::{InputStrategy, ReactionCriteria};
use crate::computation_graph::scheduler::{AccumulatorDeclaration, ReactorDeclaration};
use cloacina_computation_graph::{CompiledGraphFn, GraphResult, InputCache};
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc;
#[tokio::test]
async fn minimal_embedded_author_fires() {
let fires = Arc::new(AtomicU32::new(0));
let fires_in_graph = fires.clone();
let graph_fn: CompiledGraphFn = Arc::new(move |_cache: InputCache| {
let fires = fires_in_graph.clone();
Box::pin(async move {
fires.fetch_add(1, Ordering::SeqCst);
GraphResult::completed(vec![])
})
});
let decl = ComputationGraphDeclaration {
name: "embedded_min".to_string(),
accumulators: vec![AccumulatorDeclaration {
name: "events".to_string(),
factory: Arc::new(PassthroughAccumulatorFactory),
}],
reactor: ReactorDeclaration {
criteria: ReactionCriteria::WhenAny,
strategy: InputStrategy::Latest,
graph_fn,
constructor: None,
},
tenant_id: None,
reactor_name: None,
topology: None,
};
let graph = EmbeddedGraph::spawn(decl).await.expect("spawn");
graph
.push("events", &serde_json::json!({"value": 42.0}))
.await
.expect("push");
for _ in 0..50 {
if fires.load(Ordering::SeqCst) > 0 {
break;
}
tokio::time::sleep(std::time::Duration::from_millis(20)).await;
}
assert!(
fires.load(Ordering::SeqCst) > 0,
"pushed event must fire the reactor through the embedded builder"
);
graph.shutdown().await;
}
}