haystack_server/ops/
point_write.rs1use actix_web::{HttpRequest, HttpResponse, web};
30
31use haystack_core::data::{HDict, HGrid};
32use haystack_core::kinds::Kind;
33
34use crate::content;
35use crate::error::HaystackError;
36use crate::state::AppState;
37
38pub async fn handle(
43 req: HttpRequest,
44 body: String,
45 state: web::Data<AppState>,
46) -> Result<HttpResponse, HaystackError> {
47 let content_type = req
48 .headers()
49 .get("Content-Type")
50 .and_then(|v| v.to_str().ok())
51 .unwrap_or("");
52 let accept = req
53 .headers()
54 .get("Accept")
55 .and_then(|v| v.to_str().ok())
56 .unwrap_or("");
57
58 let request_grid = content::decode_request_grid(&body, content_type)
59 .map_err(|e| HaystackError::bad_request(format!("failed to decode request: {e}")))?;
60
61 for row in request_grid.rows.iter() {
62 let ref_val = match row.get("id") {
63 Some(Kind::Ref(r)) => r.val.clone(),
64 _ => continue,
65 };
66
67 let level = match row.get("level") {
68 Some(Kind::Number(n)) => n.val as u32,
69 _ => 17, };
71
72 if !(1..=17).contains(&level) {
73 return Err(HaystackError::bad_request(format!(
74 "level must be between 1 and 17, got {level}"
75 )));
76 }
77
78 if !state.graph.contains(&ref_val) {
80 if let Some(connector) = state.federation.owner_of(&ref_val) {
81 let val = row.get("val").cloned().unwrap_or(Kind::Null);
82 connector
83 .proxy_point_write(&ref_val, level as u8, &val)
84 .await
85 .map_err(|e| HaystackError::internal(format!("federation proxy error: {e}")))?;
86 continue;
87 }
88 return Err(HaystackError::not_found(format!(
89 "entity not found: {ref_val}"
90 )));
91 }
92
93 let entity = state
95 .graph
96 .get(&ref_val)
97 .ok_or_else(|| HaystackError::not_found(format!("entity not found: {ref_val}")))?;
98 if !entity.has("writable") {
99 return Err(HaystackError::bad_request(format!(
100 "entity '{ref_val}' is not writable"
101 )));
102 }
103
104 if let Some(val) = row.get("val") {
106 let mut changes = HDict::new();
108 changes.set("curVal", val.clone());
109 changes.set(
110 "writeLevel",
111 Kind::Number(haystack_core::kinds::Number::unitless(level as f64)),
112 );
113 state
114 .graph
115 .update(&ref_val, changes)
116 .map_err(|e| HaystackError::bad_request(format!("write failed: {e}")))?;
117 }
118 }
119
120 let grid = HGrid::new();
122 let (encoded, ct) = content::encode_response_grid(&grid, accept)
123 .map_err(|e| HaystackError::internal(format!("encoding error: {e}")))?;
124
125 Ok(HttpResponse::Ok().content_type(ct).body(encoded))
126}