haystack_server/ops/
invoke.rs1use actix_web::{HttpRequest, HttpResponse, web};
27
28use haystack_core::kinds::Kind;
29
30use crate::content;
31use crate::error::HaystackError;
32use crate::state::AppState;
33
34pub async fn handle(
39 req: HttpRequest,
40 body: String,
41 state: web::Data<AppState>,
42) -> Result<HttpResponse, HaystackError> {
43 let content_type = req
44 .headers()
45 .get("Content-Type")
46 .and_then(|v| v.to_str().ok())
47 .unwrap_or("");
48 let accept = req
49 .headers()
50 .get("Accept")
51 .and_then(|v| v.to_str().ok())
52 .unwrap_or("");
53
54 let request_grid = content::decode_request_grid(&body, content_type)
55 .map_err(|e| HaystackError::bad_request(format!("failed to decode request: {e}")))?;
56
57 let row = request_grid
59 .row(0)
60 .ok_or_else(|| HaystackError::bad_request("request grid has no rows"))?;
61
62 let ref_val = match row.get("id") {
63 Some(Kind::Ref(r)) => &r.val,
64 _ => {
65 return Err(HaystackError::bad_request(
66 "request row must have an 'id' column with a Ref value",
67 ));
68 }
69 };
70
71 let action = match row.get("action") {
72 Some(Kind::Str(s)) => s.as_str(),
73 _ => {
74 return Err(HaystackError::bad_request(
75 "request row must have an 'action' column with a Str value",
76 ));
77 }
78 };
79
80 if !state.graph.contains(ref_val) {
82 if let Some(connector) = state.federation.owner_of(ref_val) {
83 let args = row.clone();
84 let grid = connector
85 .proxy_invoke_action(ref_val, action, args)
86 .await
87 .map_err(|e| HaystackError::internal(format!("federation proxy error: {e}")))?;
88 let (encoded, ct) = content::encode_response_grid(&grid, accept)
89 .map_err(|e| HaystackError::internal(format!("encoding error: {e}")))?;
90 return Ok(HttpResponse::Ok().content_type(ct).body(encoded));
91 }
92 return Err(HaystackError::not_found(format!(
93 "entity not found: {ref_val}"
94 )));
95 }
96
97 let entity = state
99 .graph
100 .get(ref_val)
101 .ok_or_else(|| HaystackError::not_found(format!("entity not found: {ref_val}")))?;
102
103 let args = row.clone();
105
106 log::info!("invokeAction: id={ref_val} action={action}");
107
108 let result_grid = state
110 .actions
111 .invoke(&entity, action, &args)
112 .map_err(HaystackError::bad_request)?;
113
114 let (encoded, ct) = content::encode_response_grid(&result_grid, accept)
115 .map_err(|e| HaystackError::internal(format!("encoding error: {e}")))?;
116
117 Ok(HttpResponse::Ok().content_type(ct).body(encoded))
118}