1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4use crate::{
5 AuthRequest, AuthenticatedSession, ServiceCoreEffect, ServiceCoreInput, ServiceCoreOutput,
6};
7use std::collections::HashMap;
8
9pub const EXTERNAL_CORE_PROTOCOL_VERSION: u32 = 7;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct ExternalCoreRequest {
13 pub id: String,
14 pub method: ExternalCoreMethod,
15 pub payload: Value,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct ExternalCoreResponse {
20 pub id: String,
21 pub ok: bool,
22 #[serde(skip_serializing_if = "Option::is_none")]
23 pub result: Option<Value>,
24 #[serde(skip_serializing_if = "Option::is_none")]
25 pub error: Option<ExternalCoreError>,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct ExternalCoreError {
30 pub code: String,
31 pub message: String,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub enum ExternalCoreMethod {
36 HandleCoreInput,
37 RegisterLocalAppClient,
38 RegisterNodeConnection,
39 CleanupWsState,
40 UpdateCallbackBase,
41 DeviceIdentity,
42 CommissionFingerprint,
43 CommissionPublicKeyBase64,
44 ListMdnsRecords,
45 PollEvents,
46 CompleteEvents,
47 CommissionChallengePayload,
48 PairingStartPayload,
49 SetupStartPayload,
50 GeneralWebhookPayload,
51 PlaygroundWebhookPayload,
52 InvokeWasmPayload,
53 Health,
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct HandleCoreInputRequest {
58 pub input: ServiceCoreInput,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct HandleCoreInputResponse {
63 pub output: ServiceCoreOutput,
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct RegisterLocalAppClientRequest {
68 pub ws_id: String,
69 pub auth_request: AuthRequest,
70 pub session: AuthenticatedSession,
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct RegisterNodeConnectionRequest {
75 pub ws_id: String,
76 pub session_id: String,
77 pub node_type: String,
78 pub instance_id: String,
79 #[serde(default, skip_serializing_if = "Option::is_none")]
80 pub host_name: Option<String>,
81 pub tenant_id: String,
82 pub scope_id: String,
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct CleanupWsStateRequest {
87 pub ws_id: String,
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct UpdateCallbackBaseRequest {
92 pub callback_base: String,
93 pub host: String,
94 pub port: u16,
95}
96
97#[derive(Debug, Clone, Serialize, Deserialize)]
98#[serde(rename_all = "camelCase")]
99pub struct ExternalCoreDeviceIdentity {
100 pub device_id: String,
101 pub device_name: String,
102}
103
104#[derive(Debug, Clone, Serialize, Deserialize)]
105#[serde(rename_all = "camelCase")]
106pub struct ListMdnsRecordsRequest {
107 pub port: u16,
108 pub addresses: Vec<String>,
109 pub host_type: String,
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize)]
113#[serde(rename_all = "camelCase")]
114pub struct ExternalCoreMdnsRecord {
115 pub service_type: String,
116 pub instance_name: String,
117 pub port: u16,
118 pub properties: HashMap<String, String>,
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize)]
122#[serde(rename_all = "camelCase")]
123pub struct PollExternalCoreEventsRequest {
124 pub consumer_id: String,
125 pub max_events: u16,
126 pub timeout_ms: u64,
127}
128
129#[derive(Debug, Clone, Serialize, Deserialize)]
130#[serde(rename_all = "camelCase")]
131pub struct PollExternalCoreEventsResponse {
132 pub events: Vec<ExternalCoreEvent>,
133 pub timed_out: bool,
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize)]
137#[serde(rename_all = "camelCase")]
138pub struct CompleteExternalCoreEventsRequest {
139 pub consumer_id: String,
140 pub completions: Vec<ExternalCoreEventCompletion>,
141}
142
143#[derive(Debug, Clone, Serialize, Deserialize)]
144#[serde(rename_all = "camelCase")]
145pub struct CompleteExternalCoreEventsResponse {
146 pub completed_event_ids: Vec<String>,
147 pub missing_event_ids: Vec<String>,
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize)]
151#[serde(rename_all = "camelCase")]
152pub struct ExternalCoreEvent {
153 pub event_id: String,
154 pub kind: ExternalCoreEventKind,
155 pub payload: Value,
156 pub expects_response: bool,
157}
158
159#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
160#[serde(rename_all = "camelCase")]
161pub enum ExternalCoreEventKind {
162 MdnsRecordsChanged,
163 ServiceEffects,
164 ScopeOwnedDataPurgeRequested,
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
168#[serde(rename_all = "camelCase")]
169pub struct ScopeOwnedDataPurgeRequest {
170 pub tenant_id: String,
171 pub scope_id: String,
172}
173
174#[derive(Debug, Clone, Serialize, Deserialize)]
175#[serde(rename_all = "camelCase")]
176pub struct ExternalCoreServiceEffects {
177 pub effects: Vec<ServiceCoreEffect>,
178}
179
180#[derive(Debug, Clone, Serialize, Deserialize)]
181#[serde(rename_all = "camelCase")]
182pub struct ExternalCoreEventCompletion {
183 pub event_id: String,
184 pub ok: bool,
185 #[serde(default, skip_serializing_if = "Option::is_none")]
186 pub response: Option<Value>,
187 #[serde(default, skip_serializing_if = "Option::is_none")]
188 pub error: Option<ExternalCoreError>,
189}
190
191#[derive(Debug, Clone, Serialize, Deserialize)]
192pub struct HttpPayloadRequest {
193 pub payload: Value,
194}
195
196#[derive(Debug, Clone, Serialize, Deserialize)]
197pub struct EndpointPayloadRequest {
198 pub endpoint_id: String,
199 pub payload: Value,
200}
201
202#[derive(Debug, Clone, Serialize, Deserialize)]
203pub struct PlaygroundPayloadRequest {
204 pub tenant_id: String,
205 pub scope_id: String,
206 pub device_id: String,
207 pub capability_id: String,
208 pub payload: Value,
209}
210
211#[derive(Debug, Clone, Serialize, Deserialize)]
212#[serde(rename_all = "camelCase")]
213pub struct ExternalCoreRuntimeMetadata {
214 pub backend: String,
215 pub instance_id: String,
216 pub pid: u32,
217 pub state: String,
218 pub started_at: String,
219 pub protocol_version: u32,
220 pub binary_version: String,
221 pub socket_path: String,
222}
223
224#[derive(Debug, Clone, Serialize, Deserialize)]
225#[serde(rename_all = "camelCase")]
226pub struct ExternalCoreHealth {
227 pub ok: bool,
228 pub ready: bool,
229 pub backend: String,
230 pub binary_version: String,
231 pub agent_version: String,
232 pub protocol_version: u32,
233 #[serde(skip_serializing_if = "Option::is_none")]
234 pub instance_id: Option<String>,
235 #[serde(skip_serializing_if = "Option::is_none")]
236 pub pid: Option<u32>,
237 #[serde(skip_serializing_if = "Option::is_none")]
238 pub state: Option<String>,
239 #[serde(skip_serializing_if = "Option::is_none")]
240 pub started_at: Option<String>,
241 #[serde(default)]
242 pub capabilities: Vec<String>,
243}
244
245#[cfg(test)]
246mod tests {
247 use super::*;
248
249 #[test]
250 fn scope_owned_data_purge_event_uses_the_v7_wire_shape() {
251 let event = ExternalCoreEvent {
252 event_id: "event-1".to_string(),
253 kind: ExternalCoreEventKind::ScopeOwnedDataPurgeRequested,
254 payload: serde_json::to_value(ScopeOwnedDataPurgeRequest {
255 tenant_id: "tenant-1".to_string(),
256 scope_id: "scope-1".to_string(),
257 })
258 .unwrap(),
259 expects_response: true,
260 };
261
262 let value = serde_json::to_value(event).unwrap();
263 assert_eq!(EXTERNAL_CORE_PROTOCOL_VERSION, 7);
264 assert_eq!(value["kind"], "scopeOwnedDataPurgeRequested");
265 assert_eq!(value["payload"]["tenantId"], "tenant-1");
266 assert_eq!(value["payload"]["scopeId"], "scope-1");
267 assert_eq!(value["expectsResponse"], true);
268 }
269}