aboutsummaryrefslogtreecommitdiff
path: root/src/client/endpoint/control.rs
diff options
context:
space:
mode:
authorakbash <akbash@herdr.dev>2026-09-14 01:11:07 +0300
committerGitHub <noreply@github.com>2026-09-14 02:11:07 +0400
commit3b478e69dcbe8e416ddd46f0e24156392d4e71da (patch)
treee4ae8adfc09dce6eb09bd036fb727cc7f19edff7 /src/client/endpoint/control.rs
parent4e1096b1018496a23fb9de7f9ae737d3792bb38b (diff)
fix: scope agent views to the selected machine (#3784)
* fix: scope agent views to the selected machine refs #3732 * fix: preserve stable agent view ordering refs #3732 --------- Co-authored-by: akbash-bot <300245827+akbash-bot@users.noreply.github.com> Co-authored-by: JJ Liebig <jonathan.liebig@gmail.com>
Diffstat (limited to 'src/client/endpoint/control.rs')
-rw-r--r--src/client/endpoint/control.rs91
1 files changed, 91 insertions, 0 deletions
diff --git a/src/client/endpoint/control.rs b/src/client/endpoint/control.rs
index 7cf1159f..6eef8dc8 100644
--- a/src/client/endpoint/control.rs
+++ b/src/client/endpoint/control.rs
@@ -1,7 +1,14 @@
use super::ClientEndpointId;
+pub(crate) struct DecodedAgentViewProjection {
+ pub(crate) boot_id: String,
+ pub(crate) revision: u64,
+ pub(crate) view: Result<Option<crate::api::schema::AgentViewSetParams>, ()>,
+}
+
pub(crate) enum EndpointControlMessage {
HealthPong,
+ AgentViewProjection(DecodedAgentViewProjection),
Snapshot(Box<crate::protocol::ClientShellSnapshot>),
Ignored,
}
@@ -13,6 +20,33 @@ pub(crate) fn decode_endpoint_control(
if kind == crate::protocol::endpoint::HEALTH_PONG_KIND {
return Ok(EndpointControlMessage::HealthPong);
}
+ if kind == crate::protocol::endpoint::AGENT_VIEW_PROJECTION_KIND {
+ let Ok(projection): Result<crate::protocol::endpoint::EndpointAgentViewProjection, _> =
+ serde_json::from_str(data)
+ else {
+ return Ok(EndpointControlMessage::Ignored);
+ };
+ let view = match projection.view {
+ Some(value) => serde_json::from_value(value)
+ .map(Some)
+ .map_err(|_| ())
+ .and_then(|mut view| {
+ view.as_mut()
+ .map(crate::app::agent_view::validate_agent_view)
+ .transpose()
+ .map(|_| view)
+ .map_err(|_| ())
+ }),
+ None => Ok(None),
+ };
+ return Ok(EndpointControlMessage::AgentViewProjection(
+ DecodedAgentViewProjection {
+ boot_id: projection.boot_id,
+ revision: projection.revision,
+ view,
+ },
+ ));
+ }
if kind == crate::protocol::endpoint::ENDPOINT_SNAPSHOT_KIND {
let snapshot = serde_json::from_str(data)
.map_err(|error| format!("invalid endpoint snapshot: {error}"))?;
@@ -44,6 +78,63 @@ mod tests {
}
#[test]
+ fn agent_view_projection_decodes_and_validates_the_view() {
+ let view = crate::api::schema::AgentViewSetParams {
+ source: "example.views".into(),
+ label: Some("focus".into()),
+ filter: None,
+ sort: Vec::new(),
+ };
+ let crate::protocol::ServerMessage::EndpointControl { kind, data } =
+ crate::protocol::endpoint::agent_view_projection_message("boot", 4, Some(&view))
+ .unwrap()
+ else {
+ panic!("projection control");
+ };
+ let EndpointControlMessage::AgentViewProjection(decoded) =
+ decode_endpoint_control(&kind, &data).unwrap()
+ else {
+ panic!("decoded projection");
+ };
+ assert_eq!(decoded.boot_id, "boot");
+ assert_eq!(decoded.revision, 4);
+ assert_eq!(decoded.view, Ok(Some(view)));
+ }
+
+ #[test]
+ fn unsupported_agent_view_payload_falls_back_without_rejecting_endpoint() {
+ let projection = crate::protocol::endpoint::EndpointAgentViewProjection {
+ boot_id: "boot".into(),
+ revision: 5,
+ view: Some(serde_json::json!({
+ "source": "example.views",
+ "filter": {"op": "future_filter"}
+ })),
+ };
+ let decoded = decode_endpoint_control(
+ crate::protocol::endpoint::AGENT_VIEW_PROJECTION_KIND,
+ &serde_json::to_string(&projection).unwrap(),
+ )
+ .unwrap();
+ let EndpointControlMessage::AgentViewProjection(decoded) = decoded else {
+ panic!("decoded projection");
+ };
+ assert_eq!(decoded.view, Err(()));
+ }
+
+ #[test]
+ fn malformed_agent_view_envelope_is_ignored() {
+ assert!(matches!(
+ decode_endpoint_control(
+ crate::protocol::endpoint::AGENT_VIEW_PROJECTION_KIND,
+ "not json"
+ )
+ .unwrap(),
+ EndpointControlMessage::Ignored
+ ));
+ }
+
+ #[test]
fn unknown_snapshot_codecs_are_rejected() {
assert_eq!(
decode_endpoint_control("shell.snapshot.v2", "{}")