Skip to Content
DocumentationSoftware Development Kit

Software Development Kit

Getting Started

saasexpress operator init [op-name]

This will create a Rust project structure like the one below.

saasexpress_[op-name]/ ├── Cargo.toml # Project configuration, dependencies, metadata ├── Cargo.lock # Lock file for deterministic builds (auto-generated) ├── src/ # Source code directory ├── main.rs # Entry point for binary projects ├── lib.rs # Entry point for library projects └── operators/ # Utility functions ├── [op-name].rs # Module declaration file └── .gitignore # Git ignore file

Graph “Service Activator”

Data Model

pub struct Graph { pub name: String, pub start_node: String, /// Collection of nodes in the Graph, indexed by their unique ID pub nodes: HashMap<String, Arc<Mutex<dyn Operator + 'static>>>, /// Mapping of node IDs to their outgoing edges (children) edges: HashMap<String, HashSet<String>>, pub processor: Option<Arc<Mutex<BasicProcessor>>>, ports: Ports, }

Interfaces

pub trait Graph { pub fn init(&mut self) -> &mut Self; pub fn no_processor(&mut self) -> &mut Self; pub fn init_ports(&mut self); pub fn add_edge(&mut self, from: String, to: String) -> &mut Self; pub fn add_node<O>(&mut self, id: &str, mut operator: O) -> &mut Self; } pub trait GraphRun { async fn end_to_end(&mut self, message: Vec<u8>) -> Message; async fn end_to_end_2(&mut self, message: Vec<u8>) -> Message; async fn process(&mut self, message: Message) -> Message; }

lifecycle

  • new : New graph
  • add_node_to_graph : Adds new node Operators and calls node.init()
  • add_edges
  • control(Message::Init{}) : Call Init for each Operator
  • finalize : Called when all graphs are registered

Operator

Data Model

Depends on the Operator.

Interfaces

fn _type(&self) -> OperatorType

Specify the type of operator. Valid values are:

  • Endpoint : Used when we want to have full control of the messaging flow
  • Filter : Has a convenience handler for automatically passing messages onto the next node

fn name(&self) -> String

Sets the name of the Operator.

fn handle(&self, message: Message) -> Message

The message handler. For a full list of message types see: concepts/messages.

The message returned is forwarded to the child nodes. If there are no child nodes, then it is forwarded to the origin.respond_to or origin.mpsc_respond_to channels.

fn control(&mut self, message: Message)

Handles commands that manage the lifecycle of the Operator for the particular node.

In particular the MessageType::Init is used to allow the Operator to setup the communication channels with its edge nodes.

fn init(&mut self, graph: &mut Graph)

Used for initializing the Operator for the Node when the Operator needs the full Graph to complete initialization.

Full interface

pub trait Operator: Send + Sync + Debug { fn _type(&self) -> OperatorType; fn name(&self) -> String; //fn meta(&self) -> NodeMeta; fn handle(&self, message: Message) -> Message; fn control(&mut self, message: Message); fn send(&self, message: Message); fn get(&self) -> Option<Arc<dyn AsyncHandleTrait>>; fn wait(&self) -> Message; fn init(&mut self, graph: &mut Graph); fn send_ptr(&self, _message: Arc<Message>) { let message = _message.to_owned(); self.next_ptr(self.handle_ptr(message)); } fn handle_ptr(&self, message: Arc<Message>) -> Arc<Message> { debug!("default handle (passthrough)... {}", self.name()); return message; } fn next_ptr(&self, message: Arc<Message>) { // Sending message to next operator for n in self.get_output_channels() { n.lock().unwrap().send_ptr(message.to_owned()); //break; } } fn get_output_channels(&self) -> &Vec<Arc<Mutex<dyn Operator>>>; }