use crate::graph::{
ClusterType, EdgeType, GraphCluster, GraphData, GraphEdge, GraphNode, NodeType, Protocol,
};
use crate::intelligent_behavior::rules::StateMachine;
use crate::request_chaining::ChainDefinition;
use serde_json::Value;
use std::collections::HashMap;
pub fn convert_protocol(protocol: &str) -> Protocol {
match protocol.to_lowercase().as_str() {
"http" => Protocol::Http,
"grpc" => Protocol::Grpc,
"websocket" => Protocol::Websocket,
"graphql" => Protocol::Graphql,
"mqtt" => Protocol::Mqtt,
"smtp" => Protocol::Smtp,
"kafka" => Protocol::Kafka,
"amqp" => Protocol::Amqp,
"ftp" => Protocol::Ftp,
_ => Protocol::Http, }
}
pub struct GraphBuilder {
graph: GraphData,
endpoint_to_node: HashMap<String, String>,
chain_to_cluster: HashMap<String, String>,
}
impl GraphBuilder {
pub fn new() -> Self {
Self {
graph: GraphData::new(),
endpoint_to_node: HashMap::new(),
chain_to_cluster: HashMap::new(),
}
}
pub fn build(self) -> GraphData {
self.graph
}
pub fn add_endpoint(
&mut self,
endpoint_id: String,
name: String,
protocol: Protocol,
metadata: HashMap<String, Value>,
) {
let node_id = format!("endpoint:{}", endpoint_id);
self.endpoint_to_node.insert(endpoint_id.clone(), node_id.clone());
let node = GraphNode {
id: node_id,
label: name,
node_type: NodeType::Endpoint,
protocol: Some(protocol),
current_state: None,
metadata,
};
self.graph.add_node(node);
}
pub fn add_chain(&mut self, chain: &ChainDefinition) {
let cluster_id = format!("chain:{}", chain.id);
let cluster = GraphCluster {
id: cluster_id.clone(),
label: chain.name.clone(),
cluster_type: ClusterType::Chain,
node_ids: Vec::new(),
metadata: {
let mut meta = HashMap::new();
meta.insert("chain_id".to_string(), Value::String(chain.id.clone()));
meta.insert(
"description".to_string(),
Value::String(chain.description.clone().unwrap_or_default()),
);
meta
},
};
self.graph.add_cluster(cluster);
self.chain_to_cluster.insert(chain.id.clone(), cluster_id.clone());
for link in &chain.links {
let link_node_id = format!("chain_link:{}:{}", chain.id, link.request.id);
let link_node = GraphNode {
id: link_node_id.clone(),
label: format!("{} ({})", link.request.id, link.request.method),
node_type: NodeType::Endpoint,
protocol: Some(Protocol::Http), current_state: None,
metadata: {
let mut meta = HashMap::new();
meta.insert("chain_id".to_string(), Value::String(chain.id.clone()));
meta.insert("request_id".to_string(), Value::String(link.request.id.clone()));
meta.insert("method".to_string(), Value::String(link.request.method.clone()));
meta.insert("url".to_string(), Value::String(link.request.url.clone()));
meta
},
};
self.graph.add_node(link_node);
if let Some(cluster) = self.graph.clusters.iter_mut().find(|c| c.id == cluster_id) {
cluster.node_ids.push(link_node_id.clone());
}
for dep_id in &link.request.depends_on {
let dep_node_id = format!("chain_link:{}:{}", chain.id, dep_id);
let edge = GraphEdge {
from: dep_node_id,
to: link_node_id.clone(),
edge_type: EdgeType::Dependency,
label: Some("depends on".to_string()),
metadata: HashMap::new(),
};
self.graph.add_edge(edge);
}
}
}
pub fn add_state_transition(
&mut self,
from_node_id: String,
to_node_id: String,
transition_label: Option<String>,
) {
let edge = GraphEdge {
from: from_node_id,
to: to_node_id,
edge_type: EdgeType::StateTransition,
label: transition_label,
metadata: HashMap::new(),
};
self.graph.add_edge(edge);
}
pub fn add_service_call(
&mut self,
from_node_id: String,
to_node_id: String,
call_label: Option<String>,
) {
let edge = GraphEdge {
from: from_node_id,
to: to_node_id,
edge_type: EdgeType::ServiceCall,
label: call_label,
metadata: HashMap::new(),
};
self.graph.add_edge(edge);
}
pub fn add_workspace(
&mut self,
workspace_id: String,
workspace_name: String,
endpoint_ids: Vec<String>,
) {
let cluster_id = format!("workspace:{}", workspace_id);
let cluster = GraphCluster {
id: cluster_id,
label: workspace_name,
cluster_type: ClusterType::Workspace,
node_ids: endpoint_ids,
metadata: {
let mut meta = HashMap::new();
meta.insert("workspace_id".to_string(), Value::String(workspace_id));
meta
},
};
self.graph.add_cluster(cluster);
}
pub fn add_service(
&mut self,
service_id: String,
service_name: String,
endpoint_ids: Vec<String>,
) {
let cluster_id = format!("service:{}", service_id);
let cluster = GraphCluster {
id: cluster_id,
label: service_name,
cluster_type: ClusterType::Service,
node_ids: endpoint_ids,
metadata: {
let mut meta = HashMap::new();
meta.insert("service_id".to_string(), Value::String(service_id));
meta
},
};
self.graph.add_cluster(cluster);
}
pub fn update_node_state(&mut self, node_id: &str, state: String) {
if let Some(node) = self.graph.nodes.iter_mut().find(|n| n.id == node_id) {
node.current_state = Some(state);
}
}
pub fn into_graph(self) -> GraphData {
self.graph
}
pub fn from_endpoints(
&mut self,
endpoints: &[(String, String, String, String)], ) {
for (id, name, protocol_str, description) in endpoints {
let protocol = convert_protocol(protocol_str);
let mut metadata = HashMap::new();
if !description.is_empty() {
metadata.insert("description".to_string(), Value::String(description.clone()));
}
self.add_endpoint(id.clone(), name.clone(), protocol, metadata);
}
}
pub fn from_chains(&mut self, chains: &[ChainDefinition]) {
for chain in chains {
self.add_chain(chain);
}
}
pub fn from_state_machines(&mut self, state_machines: &[StateMachine]) {
for state_machine in state_machines {
for state in &state_machine.states {
let node_id = format!("state:{}:{}", state_machine.resource_type, state);
let node = GraphNode {
id: node_id.clone(),
label: format!("{} ({})", state, state_machine.resource_type),
node_type: NodeType::Endpoint,
protocol: None,
current_state: Some(state.clone()),
metadata: {
let mut meta = HashMap::new();
meta.insert(
"resource_type".to_string(),
Value::String(state_machine.resource_type.clone()),
);
meta.insert(
"is_initial".to_string(),
Value::Bool(*state == state_machine.initial_state),
);
meta
},
};
self.graph.add_node(node);
}
for transition in &state_machine.transitions {
let from_node_id =
format!("state:{}:{}", state_machine.resource_type, transition.from_state);
let to_node_id =
format!("state:{}:{}", state_machine.resource_type, transition.to_state);
self.add_state_transition(
from_node_id,
to_node_id,
Some(format!("{} → {}", transition.from_state, transition.to_state)),
);
}
}
}
}
impl Default for GraphBuilder {
fn default() -> Self {
Self::new()
}
}