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