1use crate::graph::{
10 ClusterType, EdgeType, GraphCluster, GraphData, GraphEdge, GraphNode, NodeType, Protocol,
11};
12use crate::intelligent_behavior::rules::{StateMachine, StateTransition};
13use crate::request_chaining::ChainDefinition;
14use serde_json::Value;
15use std::collections::HashMap;
16
17pub fn convert_protocol(protocol: &str) -> Protocol {
19 match protocol.to_lowercase().as_str() {
20 "http" => Protocol::Http,
21 "grpc" => Protocol::Grpc,
22 "websocket" => Protocol::Websocket,
23 "graphql" => Protocol::Graphql,
24 "mqtt" => Protocol::Mqtt,
25 "smtp" => Protocol::Smtp,
26 "kafka" => Protocol::Kafka,
27 "amqp" => Protocol::Amqp,
28 "ftp" => Protocol::Ftp,
29 _ => Protocol::Http, }
31}
32
33pub struct GraphBuilder {
35 graph: GraphData,
37
38 endpoint_to_node: HashMap<String, String>,
40
41 chain_to_cluster: HashMap<String, String>,
43}
44
45impl GraphBuilder {
46 pub fn new() -> Self {
48 Self {
49 graph: GraphData::new(),
50 endpoint_to_node: HashMap::new(),
51 chain_to_cluster: HashMap::new(),
52 }
53 }
54
55 pub fn build(mut self) -> GraphData {
57 self.graph
58 }
59
60 pub fn add_endpoint(
62 &mut self,
63 endpoint_id: String,
64 name: String,
65 protocol: Protocol,
66 metadata: HashMap<String, Value>,
67 ) {
68 let node_id = format!("endpoint:{}", endpoint_id);
69 self.endpoint_to_node.insert(endpoint_id.clone(), node_id.clone());
70
71 let node = GraphNode {
72 id: node_id,
73 label: name,
74 node_type: NodeType::Endpoint,
75 protocol: Some(protocol),
76 current_state: None,
77 metadata,
78 };
79
80 self.graph.add_node(node);
81 }
82
83 pub fn add_chain(&mut self, chain: &ChainDefinition) {
85 let cluster_id = format!("chain:{}", chain.id);
87 let cluster = GraphCluster {
88 id: cluster_id.clone(),
89 label: chain.name.clone(),
90 cluster_type: ClusterType::Chain,
91 node_ids: Vec::new(),
92 metadata: {
93 let mut meta = HashMap::new();
94 meta.insert("chain_id".to_string(), Value::String(chain.id.clone()));
95 meta.insert(
96 "description".to_string(),
97 Value::String(chain.description.clone().unwrap_or_default()),
98 );
99 meta
100 },
101 };
102 self.graph.add_cluster(cluster);
103 self.chain_to_cluster.insert(chain.id.clone(), cluster_id.clone());
104
105 for link in &chain.links {
107 let link_node_id = format!("chain_link:{}:{}", chain.id, link.request.id);
108
109 let link_node = GraphNode {
112 id: link_node_id.clone(),
113 label: format!("{} ({})", link.request.id, link.request.method),
114 node_type: NodeType::Endpoint,
115 protocol: Some(Protocol::Http), current_state: None,
117 metadata: {
118 let mut meta = HashMap::new();
119 meta.insert("chain_id".to_string(), Value::String(chain.id.clone()));
120 meta.insert("request_id".to_string(), Value::String(link.request.id.clone()));
121 meta.insert("method".to_string(), Value::String(link.request.method.clone()));
122 meta.insert("url".to_string(), Value::String(link.request.url.clone()));
123 meta
124 },
125 };
126 self.graph.add_node(link_node);
127
128 if let Some(cluster) = self.graph.clusters.iter_mut().find(|c| c.id == cluster_id) {
130 cluster.node_ids.push(link_node_id.clone());
131 }
132
133 for dep_id in &link.request.depends_on {
135 let dep_node_id = format!("chain_link:{}:{}", chain.id, dep_id);
136 let edge = GraphEdge {
137 from: dep_node_id,
138 to: link_node_id.clone(),
139 edge_type: EdgeType::Dependency,
140 label: Some("depends on".to_string()),
141 metadata: HashMap::new(),
142 };
143 self.graph.add_edge(edge);
144 }
145 }
146 }
147
148 pub fn add_state_transition(
150 &mut self,
151 from_node_id: String,
152 to_node_id: String,
153 transition_label: Option<String>,
154 ) {
155 let edge = GraphEdge {
156 from: from_node_id,
157 to: to_node_id,
158 edge_type: EdgeType::StateTransition,
159 label: transition_label,
160 metadata: HashMap::new(),
161 };
162 self.graph.add_edge(edge);
163 }
164
165 pub fn add_service_call(
167 &mut self,
168 from_node_id: String,
169 to_node_id: String,
170 call_label: Option<String>,
171 ) {
172 let edge = GraphEdge {
173 from: from_node_id,
174 to: to_node_id,
175 edge_type: EdgeType::ServiceCall,
176 label: call_label,
177 metadata: HashMap::new(),
178 };
179 self.graph.add_edge(edge);
180 }
181
182 pub fn add_workspace(
184 &mut self,
185 workspace_id: String,
186 workspace_name: String,
187 endpoint_ids: Vec<String>,
188 ) {
189 let cluster_id = format!("workspace:{}", workspace_id);
190 let cluster = GraphCluster {
191 id: cluster_id,
192 label: workspace_name,
193 cluster_type: ClusterType::Workspace,
194 node_ids: endpoint_ids,
195 metadata: {
196 let mut meta = HashMap::new();
197 meta.insert("workspace_id".to_string(), Value::String(workspace_id));
198 meta
199 },
200 };
201 self.graph.add_cluster(cluster);
202 }
203
204 pub fn add_service(
206 &mut self,
207 service_id: String,
208 service_name: String,
209 endpoint_ids: Vec<String>,
210 ) {
211 let cluster_id = format!("service:{}", service_id);
212 let cluster = GraphCluster {
213 id: cluster_id,
214 label: service_name,
215 cluster_type: ClusterType::Service,
216 node_ids: endpoint_ids,
217 metadata: {
218 let mut meta = HashMap::new();
219 meta.insert("service_id".to_string(), Value::String(service_id));
220 meta
221 },
222 };
223 self.graph.add_cluster(cluster);
224 }
225
226 pub fn update_node_state(&mut self, node_id: &str, state: String) {
228 if let Some(node) = self.graph.nodes.iter_mut().find(|n| n.id == node_id) {
229 node.current_state = Some(state);
230 }
231 }
232
233 pub fn into_graph(self) -> GraphData {
235 self.graph
236 }
237
238 pub fn from_endpoints(
240 &mut self,
241 endpoints: &[(String, String, String, String)], ) {
243 for (id, name, protocol_str, description) in endpoints {
244 let protocol = convert_protocol(protocol_str);
245 let mut metadata = HashMap::new();
246 if !description.is_empty() {
247 metadata.insert("description".to_string(), Value::String(description.clone()));
248 }
249
250 self.add_endpoint(id.clone(), name.clone(), protocol, metadata);
251 }
252 }
253
254 pub fn from_chains(&mut self, chains: &[ChainDefinition]) {
256 for chain in chains {
257 self.add_chain(chain);
258 }
259 }
260
261 pub fn from_state_machines(&mut self, state_machines: &[StateMachine]) {
263 for state_machine in state_machines {
264 for state in &state_machine.states {
266 let node_id = format!("state:{}:{}", state_machine.resource_type, state);
267 let node = GraphNode {
268 id: node_id.clone(),
269 label: format!("{} ({})", state, state_machine.resource_type),
270 node_type: NodeType::Endpoint,
271 protocol: None,
272 current_state: Some(state.clone()),
273 metadata: {
274 let mut meta = HashMap::new();
275 meta.insert(
276 "resource_type".to_string(),
277 Value::String(state_machine.resource_type.clone()),
278 );
279 meta.insert(
280 "is_initial".to_string(),
281 Value::Bool(*state == state_machine.initial_state),
282 );
283 meta
284 },
285 };
286 self.graph.add_node(node);
287 }
288
289 for transition in &state_machine.transitions {
291 let from_node_id =
292 format!("state:{}:{}", state_machine.resource_type, transition.from_state);
293 let to_node_id =
294 format!("state:{}:{}", state_machine.resource_type, transition.to_state);
295
296 self.add_state_transition(
297 from_node_id,
298 to_node_id,
299 Some(format!("{} → {}", transition.from_state, transition.to_state)),
300 );
301 }
302 }
303 }
304}
305
306impl Default for GraphBuilder {
307 fn default() -> Self {
308 Self::new()
309 }
310}