vta_sdk/protocols/
device_management.rs1use serde::{Deserialize, Serialize};
30use serde_json::Value;
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
34#[serde(rename_all = "camelCase")]
35pub struct DeviceRegisterBody {
36 pub consumer_kind: Value,
41 pub display_name: String,
42 #[serde(default, skip_serializing_if = "Option::is_none")]
43 pub platform: Option<String>,
44 #[serde(default, skip_serializing_if = "Option::is_none")]
45 pub hpke_public_key: Option<String>,
46}
47
48#[derive(Debug, Clone, Default, Serialize, Deserialize)]
53#[serde(rename_all = "camelCase")]
54pub struct DeviceHeartbeatBody {
55 #[serde(default, skip_serializing_if = "Option::is_none")]
56 pub platform: Option<String>,
57 #[serde(default, skip_serializing_if = "Option::is_none")]
58 pub vault_seq: Option<u64>,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
63#[serde(rename_all = "camelCase")]
64pub struct DeviceDisableBody {
65 pub device_id: String,
66 #[serde(default, skip_serializing_if = "Option::is_none")]
67 pub reason: Option<String>,
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
75#[serde(rename_all = "camelCase")]
76pub struct DeviceWipeBody {
77 pub device_id: String,
78 pub scope: String,
80 pub reason: String,
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize)]
85#[serde(rename_all = "camelCase")]
86pub struct WakeHandle {
87 pub gateway: String,
88 pub handle: String,
89}
90
91#[derive(Debug, Clone, Default, Serialize, Deserialize)]
93#[serde(rename_all = "camelCase")]
94pub struct DeviceSetWakeBody {
95 #[serde(default, skip_serializing_if = "Option::is_none")]
96 pub wake_handle: Option<WakeHandle>,
97 #[serde(default, skip_serializing_if = "Option::is_none")]
98 pub suggested_triggers: Option<Vec<String>>,
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[test]
112 fn a_bare_heartbeat_is_an_empty_object() {
113 assert_eq!(
114 serde_json::to_value(DeviceHeartbeatBody::default()).expect("serialises"),
115 serde_json::json!({})
116 );
117 }
118
119 #[test]
120 fn an_unset_register_member_is_absent() {
121 let minimal = DeviceRegisterBody {
122 consumer_kind: serde_json::json!({"kind": "companion", "formFactor": "desktop"}),
123 display_name: "laptop".into(),
124 platform: None,
125 hpke_public_key: None,
126 };
127 assert_eq!(
128 serde_json::to_value(&minimal).expect("serialises"),
129 serde_json::json!({
130 "consumerKind": {"kind": "companion", "formFactor": "desktop"},
131 "displayName": "laptop",
132 })
133 );
134 }
135
136 #[test]
139 fn set_members_serialise_camel_case() {
140 let full = DeviceRegisterBody {
141 consumer_kind: serde_json::json!({"kind": "service", "serviceKind": "ai-agent"}),
142 display_name: "agent".into(),
143 platform: Some("macos".into()),
144 hpke_public_key: Some("zHpke".into()),
145 };
146 let v = serde_json::to_value(&full).expect("serialises");
147 assert_eq!(v.get("platform").and_then(Value::as_str), Some("macos"));
148 assert_eq!(
149 v.get("hpkePublicKey").and_then(Value::as_str),
150 Some("zHpke")
151 );
152 }
153
154 #[test]
155 fn a_wake_handle_nests_under_its_camel_case_member() {
156 let body = DeviceSetWakeBody {
157 wake_handle: Some(WakeHandle {
158 gateway: "apns".into(),
159 handle: "opaque".into(),
160 }),
161 suggested_triggers: Some(vec!["message".into()]),
162 };
163 assert_eq!(
164 serde_json::to_value(&body).expect("serialises"),
165 serde_json::json!({
166 "wakeHandle": {"gateway": "apns", "handle": "opaque"},
167 "suggestedTriggers": ["message"],
168 })
169 );
170 }
171}