1use iii_helpers::http::HttpInvocationConfig;
2use schemars::JsonSchema;
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use uuid::Uuid;
6
7#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
13#[serde(tag = "type", rename_all = "lowercase")]
14pub enum TriggerAction {
15 Enqueue { queue: String },
17 Void,
19}
20
21#[derive(Debug, Clone)]
52pub struct TriggerRequest {
53 pub function_id: String,
54 pub payload: Value,
55 pub action: Option<TriggerAction>,
56 pub timeout_ms: Option<u64>,
57}
58
59impl TriggerRequest {
60 pub fn metadata(self, metadata: Value) -> TriggerRequestWithMetadata {
63 TriggerRequestWithMetadata {
64 request: self,
65 metadata: Some(metadata),
66 }
67 }
68}
69
70#[derive(Debug, Clone)]
72pub struct TriggerRequestWithMetadata {
73 pub(crate) request: TriggerRequest,
74 pub(crate) metadata: Option<Value>,
75}
76
77impl<T> From<T> for TriggerRequestWithMetadata
78where
79 T: Into<TriggerRequest>,
80{
81 fn from(request: T) -> Self {
82 Self {
83 request: request.into(),
84 metadata: None,
85 }
86 }
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize)]
90#[serde(tag = "type", rename_all = "lowercase")]
91pub enum Message {
92 RegisterTriggerType {
93 id: String,
94 description: String,
95 #[serde(skip_serializing_if = "Option::is_none")]
96 trigger_request_format: Option<Value>,
97 #[serde(skip_serializing_if = "Option::is_none")]
98 call_request_format: Option<Value>,
99 },
100 RegisterTrigger {
101 id: String,
102 trigger_type: String,
103 function_id: String,
104 config: Value,
105 #[serde(skip_serializing_if = "Option::is_none")]
106 metadata: Option<Value>,
107 },
108 TriggerRegistrationResult {
109 id: String,
110 trigger_type: String,
111 function_id: String,
112 #[serde(skip_serializing_if = "Option::is_none")]
113 error: Option<ErrorBody>,
114 },
115 UnregisterTrigger {
116 id: String,
117 trigger_type: String,
118 },
119 UnregisterTriggerType {
120 id: String,
121 },
122 RegisterFunction {
123 id: String,
124 #[serde(skip_serializing_if = "Option::is_none")]
125 description: Option<String>,
126 #[serde(skip_serializing_if = "Option::is_none")]
127 request_format: Option<Value>,
128 #[serde(skip_serializing_if = "Option::is_none")]
129 response_format: Option<Value>,
130 #[serde(skip_serializing_if = "Option::is_none")]
131 metadata: Option<Value>,
132 #[serde(skip_serializing_if = "Option::is_none")]
133 invocation: Option<HttpInvocationConfig>,
134 },
135 UnregisterFunction {
136 id: String,
137 },
138 InvokeFunction {
139 invocation_id: Option<Uuid>,
140 function_id: String,
141 data: Value,
142 #[serde(skip_serializing_if = "Option::is_none")]
143 traceparent: Option<String>,
144 #[serde(skip_serializing_if = "Option::is_none")]
145 baggage: Option<String>,
146 #[serde(skip_serializing_if = "Option::is_none")]
147 action: Option<TriggerAction>,
148 #[serde(default, skip_serializing_if = "Option::is_none")]
152 metadata: Option<Value>,
153 },
154 InvocationResult {
155 invocation_id: Uuid,
156 function_id: String,
157 #[serde(skip_serializing_if = "Option::is_none")]
158 result: Option<Value>,
159 #[serde(skip_serializing_if = "Option::is_none")]
160 error: Option<ErrorBody>,
161 #[serde(skip_serializing_if = "Option::is_none")]
162 traceparent: Option<String>,
163 #[serde(skip_serializing_if = "Option::is_none")]
164 baggage: Option<String>,
165 },
166 Ping,
167 Pong,
168 WorkerRegistered {
169 worker_id: String,
170 },
171}
172
173#[derive(Debug, Clone, Serialize, Deserialize)]
174pub struct RegisterTriggerTypeMessage {
175 pub id: String,
176 pub description: String,
177 #[serde(skip_serializing_if = "Option::is_none")]
178 pub trigger_request_format: Option<Value>,
179 #[serde(skip_serializing_if = "Option::is_none")]
180 pub call_request_format: Option<Value>,
181}
182
183impl RegisterTriggerTypeMessage {
184 pub fn to_message(&self) -> Message {
185 Message::RegisterTriggerType {
186 id: self.id.clone(),
187 description: self.description.clone(),
188 trigger_request_format: self.trigger_request_format.clone(),
189 call_request_format: self.call_request_format.clone(),
190 }
191 }
192}
193
194#[derive(Debug, Clone, Serialize, Deserialize)]
197pub struct RegisterTriggerInput {
198 pub trigger_type: String,
199 pub function_id: String,
200 pub config: Value,
201 #[serde(skip_serializing_if = "Option::is_none")]
202 pub metadata: Option<Value>,
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize)]
206pub struct RegisterTriggerMessage {
207 pub id: String,
208 pub trigger_type: String,
209 pub function_id: String,
210 pub config: Value,
211 #[serde(skip_serializing_if = "Option::is_none")]
212 pub metadata: Option<Value>,
213}
214
215impl RegisterTriggerMessage {
216 pub fn to_message(&self) -> Message {
217 Message::RegisterTrigger {
218 id: self.id.clone(),
219 trigger_type: self.trigger_type.clone(),
220 function_id: self.function_id.clone(),
221 config: self.config.clone(),
222 metadata: self.metadata.clone(),
223 }
224 }
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize)]
228pub struct UnregisterTriggerMessage {
229 pub id: String,
230 pub trigger_type: String,
231}
232
233impl UnregisterTriggerMessage {
234 pub fn to_message(&self) -> Message {
235 Message::UnregisterTrigger {
236 id: self.id.clone(),
237 trigger_type: self.trigger_type.clone(),
238 }
239 }
240}
241
242#[derive(Debug, Clone, Serialize, Deserialize)]
243pub struct UnregisterTriggerTypeMessage {
244 pub id: String,
245}
246
247impl UnregisterTriggerTypeMessage {
248 pub fn to_message(&self) -> Message {
249 Message::UnregisterTriggerType {
250 id: self.id.clone(),
251 }
252 }
253}
254
255#[derive(Debug, Clone, Serialize, Deserialize)]
256pub struct RegisterFunctionMessage {
257 pub id: String,
258 #[serde(skip_serializing_if = "Option::is_none")]
259 pub description: Option<String>,
260 #[serde(skip_serializing_if = "Option::is_none")]
261 pub request_format: Option<Value>,
262 #[serde(skip_serializing_if = "Option::is_none")]
263 pub response_format: Option<Value>,
264 #[serde(skip_serializing_if = "Option::is_none")]
265 pub metadata: Option<Value>,
266 #[serde(skip_serializing_if = "Option::is_none")]
267 pub invocation: Option<HttpInvocationConfig>,
268}
269
270impl RegisterFunctionMessage {
271 pub fn with_id(name: String) -> Self {
272 RegisterFunctionMessage {
273 id: name,
274 description: None,
275 request_format: None,
276 response_format: None,
277 metadata: None,
278 invocation: None,
279 }
280 }
281 pub fn with_description(mut self, description: String) -> Self {
282 self.description = Some(description);
283 self
284 }
285 pub fn to_message(&self) -> Message {
286 Message::RegisterFunction {
287 id: self.id.clone(),
288 description: self.description.clone(),
289 request_format: self.request_format.clone(),
290 response_format: self.response_format.clone(),
291 metadata: self.metadata.clone(),
292 invocation: self.invocation.clone(),
293 }
294 }
295}
296
297#[derive(Debug, Clone, Serialize, Deserialize)]
298pub struct FunctionMessage {
299 pub function_id: String,
300 #[serde(skip_serializing_if = "Option::is_none")]
301 pub description: Option<String>,
302 #[serde(skip_serializing_if = "Option::is_none")]
303 pub request_format: Option<Value>,
304 #[serde(skip_serializing_if = "Option::is_none")]
305 pub response_format: Option<Value>,
306 #[serde(skip_serializing_if = "Option::is_none")]
307 pub metadata: Option<Value>,
308}
309
310#[derive(Debug, Clone, Serialize, Deserialize)]
311#[non_exhaustive]
312pub struct ErrorBody {
313 pub code: String,
314 pub message: String,
315 #[serde(skip_serializing_if = "Option::is_none")]
316 pub stacktrace: Option<String>,
317}
318
319#[cfg(test)]
320mod tests {
321 use std::collections::HashMap;
322
323 use super::*;
324
325 #[test]
326 fn register_function_to_message_and_serializes_type() {
327 let msg = RegisterFunctionMessage {
328 id: "functions.echo".to_string(),
329 description: Some("Echo function".to_string()),
330 request_format: None,
331 response_format: None,
332 metadata: None,
333 invocation: None,
334 };
335
336 let message = msg.to_message();
337 match &message {
338 Message::RegisterFunction {
339 id, description, ..
340 } => {
341 assert_eq!(id, "functions.echo");
342 assert_eq!(description.as_deref(), Some("Echo function"));
343 }
344 _ => panic!("unexpected message variant"),
345 }
346
347 let serialized = serde_json::to_value(&message).unwrap();
348 assert_eq!(serialized["type"], "registerfunction");
349 assert_eq!(serialized["id"], "functions.echo");
350 assert_eq!(serialized["description"], "Echo function");
351 }
352
353 #[test]
354 fn register_http_function_serializes_invocation() {
355 use iii_helpers::http::{HttpInvocationConfig, HttpMethod};
356
357 let msg = RegisterFunctionMessage {
358 id: "external::my_lambda".to_string(),
359 description: None,
360 request_format: None,
361 response_format: None,
362 metadata: None,
363 invocation: Some(HttpInvocationConfig {
364 url: "https://example.com/invoke".to_string(),
365 method: HttpMethod::Post,
366 timeout_ms: Some(30000),
367 headers: HashMap::new(),
368 auth: None,
369 }),
370 };
371
372 let serialized = serde_json::to_value(msg.to_message()).unwrap();
373 assert_eq!(serialized["type"], "registerfunction");
374 assert_eq!(serialized["id"], "external::my_lambda");
375 assert!(serialized["invocation"].is_object());
376 assert_eq!(
377 serialized["invocation"]["url"],
378 "https://example.com/invoke"
379 );
380 assert_eq!(serialized["invocation"]["method"], "POST");
381 }
382}