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