haystack_server/ops/
point_write.rs1use actix_web::{HttpRequest, HttpResponse, web};
4
5use haystack_core::data::{HDict, HGrid};
6use haystack_core::kinds::Kind;
7
8use crate::content;
9use crate::error::HaystackError;
10use crate::state::AppState;
11
12pub async fn handle(
17 req: HttpRequest,
18 body: String,
19 state: web::Data<AppState>,
20) -> Result<HttpResponse, HaystackError> {
21 let content_type = req
22 .headers()
23 .get("Content-Type")
24 .and_then(|v| v.to_str().ok())
25 .unwrap_or("");
26 let accept = req
27 .headers()
28 .get("Accept")
29 .and_then(|v| v.to_str().ok())
30 .unwrap_or("");
31
32 let request_grid = content::decode_request_grid(&body, content_type)
33 .map_err(|e| HaystackError::bad_request(format!("failed to decode request: {e}")))?;
34
35 for row in request_grid.rows.iter() {
36 let ref_val = match row.get("id") {
37 Some(Kind::Ref(r)) => r.val.clone(),
38 _ => continue,
39 };
40
41 let level = match row.get("level") {
42 Some(Kind::Number(n)) => n.val as u32,
43 _ => 17, };
45
46 if !(1..=17).contains(&level) {
47 return Err(HaystackError::bad_request(format!(
48 "level must be between 1 and 17, got {level}"
49 )));
50 }
51
52 if !state.graph.contains(&ref_val) {
54 if let Some(connector) = state.federation.owner_of(&ref_val) {
55 let val = row.get("val").cloned().unwrap_or(Kind::Null);
56 connector
57 .proxy_point_write(&ref_val, level as u8, &val)
58 .await
59 .map_err(|e| HaystackError::internal(format!("federation proxy error: {e}")))?;
60 continue;
61 }
62 return Err(HaystackError::not_found(format!(
63 "entity not found: {ref_val}"
64 )));
65 }
66
67 let entity = state
69 .graph
70 .get(&ref_val)
71 .ok_or_else(|| HaystackError::not_found(format!("entity not found: {ref_val}")))?;
72 if !entity.has("writable") {
73 return Err(HaystackError::bad_request(format!(
74 "entity '{ref_val}' is not writable"
75 )));
76 }
77
78 if let Some(val) = row.get("val") {
80 let mut changes = HDict::new();
82 changes.set("curVal", val.clone());
83 changes.set(
84 "writeLevel",
85 Kind::Number(haystack_core::kinds::Number::unitless(level as f64)),
86 );
87 state
88 .graph
89 .update(&ref_val, changes)
90 .map_err(|e| HaystackError::bad_request(format!("write failed: {e}")))?;
91 }
92 }
93
94 let grid = HGrid::new();
96 let (encoded, ct) = content::encode_response_grid(&grid, accept)
97 .map_err(|e| HaystackError::internal(format!("encoding error: {e}")))?;
98
99 Ok(HttpResponse::Ok().content_type(ct).body(encoded))
100}