1use std::collections::HashMap;
2
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6use uuid::Uuid;
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
9#[serde(rename_all = "UPPERCASE")]
10pub enum HttpMethod {
11 Get,
12 Post,
13 Put,
14 Patch,
15 Delete,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
24#[serde(tag = "type", rename_all = "lowercase")]
25pub enum HttpAuthConfig {
26 Hmac {
27 secret_key: String,
28 },
29 Bearer {
30 token_key: String,
31 },
32 #[serde(rename = "api_key")]
33 ApiKey {
34 header: String,
35 value_key: String,
36 },
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct HttpInvocationConfig {
43 pub url: String,
44 #[serde(default = "default_http_method")]
45 pub method: HttpMethod,
46 #[serde(skip_serializing_if = "Option::is_none")]
47 pub timeout_ms: Option<u64>,
48 #[serde(default)]
49 pub headers: HashMap<String, String>,
50 #[serde(skip_serializing_if = "Option::is_none")]
51 pub auth: Option<HttpAuthConfig>,
52}
53
54fn default_http_method() -> HttpMethod {
55 HttpMethod::Post
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
64#[serde(tag = "type", rename_all = "lowercase")]
65pub enum TriggerAction {
66 Enqueue { queue: String },
68 Void,
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
74pub struct EnqueueResult {
75 #[serde(rename = "messageReceiptId")]
76 pub message_receipt_id: String,
77}
78
79#[derive(Debug, Clone)]
102pub struct TriggerRequest {
103 pub function_id: String,
104 pub payload: Value,
105 pub action: Option<TriggerAction>,
106 pub timeout_ms: Option<u64>,
107}
108
109#[derive(Debug, Clone, Serialize, Deserialize)]
110#[serde(tag = "type", rename_all = "lowercase")]
111pub enum Message {
112 RegisterTriggerType {
113 id: String,
114 description: String,
115 #[serde(skip_serializing_if = "Option::is_none")]
116 trigger_request_format: Option<Value>,
117 #[serde(skip_serializing_if = "Option::is_none")]
118 call_request_format: Option<Value>,
119 },
120 RegisterTrigger {
121 id: String,
122 trigger_type: String,
123 function_id: String,
124 config: Value,
125 #[serde(skip_serializing_if = "Option::is_none")]
126 metadata: Option<Value>,
127 },
128 TriggerRegistrationResult {
129 id: String,
130 trigger_type: String,
131 function_id: String,
132 #[serde(skip_serializing_if = "Option::is_none")]
133 error: Option<ErrorBody>,
134 },
135 UnregisterTrigger {
136 id: String,
137 trigger_type: String,
138 },
139 UnregisterTriggerType {
140 id: String,
141 },
142 RegisterFunction {
143 id: String,
144 #[serde(skip_serializing_if = "Option::is_none")]
145 description: Option<String>,
146 #[serde(skip_serializing_if = "Option::is_none")]
147 request_format: Option<Value>,
148 #[serde(skip_serializing_if = "Option::is_none")]
149 response_format: Option<Value>,
150 #[serde(skip_serializing_if = "Option::is_none")]
151 metadata: Option<Value>,
152 #[serde(skip_serializing_if = "Option::is_none")]
153 invocation: Option<HttpInvocationConfig>,
154 },
155 UnregisterFunction {
156 id: String,
157 },
158 InvokeFunction {
159 invocation_id: Option<Uuid>,
160 function_id: String,
161 data: Value,
162 #[serde(skip_serializing_if = "Option::is_none")]
163 traceparent: Option<String>,
164 #[serde(skip_serializing_if = "Option::is_none")]
165 baggage: Option<String>,
166 #[serde(skip_serializing_if = "Option::is_none")]
167 action: Option<TriggerAction>,
168 },
169 InvocationResult {
170 invocation_id: Uuid,
171 function_id: String,
172 #[serde(skip_serializing_if = "Option::is_none")]
173 result: Option<Value>,
174 #[serde(skip_serializing_if = "Option::is_none")]
175 error: Option<ErrorBody>,
176 #[serde(skip_serializing_if = "Option::is_none")]
177 traceparent: Option<String>,
178 #[serde(skip_serializing_if = "Option::is_none")]
179 baggage: Option<String>,
180 },
181 Ping,
182 Pong,
183 WorkerRegistered {
184 worker_id: String,
185 },
186}
187
188#[derive(Debug, Clone, Serialize, Deserialize)]
189pub struct RegisterTriggerTypeMessage {
190 pub id: String,
191 pub description: String,
192 #[serde(skip_serializing_if = "Option::is_none")]
193 pub trigger_request_format: Option<Value>,
194 #[serde(skip_serializing_if = "Option::is_none")]
195 pub call_request_format: Option<Value>,
196}
197
198impl RegisterTriggerTypeMessage {
199 pub fn to_message(&self) -> Message {
200 Message::RegisterTriggerType {
201 id: self.id.clone(),
202 description: self.description.clone(),
203 trigger_request_format: self.trigger_request_format.clone(),
204 call_request_format: self.call_request_format.clone(),
205 }
206 }
207}
208
209#[derive(Debug, Clone, Serialize, Deserialize)]
212pub struct RegisterTriggerInput {
213 pub trigger_type: String,
214 pub function_id: String,
215 pub config: Value,
216 #[serde(skip_serializing_if = "Option::is_none")]
217 pub metadata: Option<Value>,
218}
219
220#[derive(Debug, Clone, Serialize, Deserialize)]
221pub struct RegisterTriggerMessage {
222 pub id: String,
223 pub trigger_type: String,
224 pub function_id: String,
225 pub config: Value,
226 #[serde(skip_serializing_if = "Option::is_none")]
227 pub metadata: Option<Value>,
228}
229
230impl RegisterTriggerMessage {
231 pub fn to_message(&self) -> Message {
232 Message::RegisterTrigger {
233 id: self.id.clone(),
234 trigger_type: self.trigger_type.clone(),
235 function_id: self.function_id.clone(),
236 config: self.config.clone(),
237 metadata: self.metadata.clone(),
238 }
239 }
240}
241
242#[derive(Debug, Clone, Serialize, Deserialize)]
243pub struct UnregisterTriggerMessage {
244 pub id: String,
245 pub trigger_type: String,
246}
247
248impl UnregisterTriggerMessage {
249 pub fn to_message(&self) -> Message {
250 Message::UnregisterTrigger {
251 id: self.id.clone(),
252 trigger_type: self.trigger_type.clone(),
253 }
254 }
255}
256
257#[derive(Debug, Clone, Serialize, Deserialize)]
258pub struct UnregisterTriggerTypeMessage {
259 pub id: String,
260}
261
262impl UnregisterTriggerTypeMessage {
263 pub fn to_message(&self) -> Message {
264 Message::UnregisterTriggerType {
265 id: self.id.clone(),
266 }
267 }
268}
269
270#[derive(Debug, Clone, Serialize, Deserialize)]
271pub struct RegisterFunctionMessage {
272 pub id: String,
273 #[serde(skip_serializing_if = "Option::is_none")]
274 pub description: Option<String>,
275 #[serde(skip_serializing_if = "Option::is_none")]
276 pub request_format: Option<Value>,
277 #[serde(skip_serializing_if = "Option::is_none")]
278 pub response_format: Option<Value>,
279 #[serde(skip_serializing_if = "Option::is_none")]
280 pub metadata: Option<Value>,
281 #[serde(skip_serializing_if = "Option::is_none")]
282 pub invocation: Option<HttpInvocationConfig>,
283}
284
285impl RegisterFunctionMessage {
286 pub fn with_id(name: String) -> Self {
287 RegisterFunctionMessage {
288 id: name,
289 description: None,
290 request_format: None,
291 response_format: None,
292 metadata: None,
293 invocation: None,
294 }
295 }
296 pub fn with_description(mut self, description: String) -> Self {
297 self.description = Some(description);
298 self
299 }
300 pub fn to_message(&self) -> Message {
301 Message::RegisterFunction {
302 id: self.id.clone(),
303 description: self.description.clone(),
304 request_format: self.request_format.clone(),
305 response_format: self.response_format.clone(),
306 metadata: self.metadata.clone(),
307 invocation: self.invocation.clone(),
308 }
309 }
310}
311
312#[derive(Debug, Clone, Serialize, Deserialize)]
313pub struct FunctionMessage {
314 pub function_id: String,
315 #[serde(skip_serializing_if = "Option::is_none")]
316 pub description: Option<String>,
317 #[serde(skip_serializing_if = "Option::is_none")]
318 pub request_format: Option<Value>,
319 #[serde(skip_serializing_if = "Option::is_none")]
320 pub response_format: Option<Value>,
321 #[serde(skip_serializing_if = "Option::is_none")]
322 pub metadata: Option<Value>,
323}
324
325#[derive(Debug, Clone, Serialize, Deserialize)]
326#[non_exhaustive]
327pub struct ErrorBody {
328 pub code: String,
329 pub message: String,
330 #[serde(skip_serializing_if = "Option::is_none")]
331 pub stacktrace: Option<String>,
332}
333
334#[cfg(test)]
335mod tests {
336 use super::*;
337
338 #[test]
339 fn register_function_to_message_and_serializes_type() {
340 let msg = RegisterFunctionMessage {
341 id: "functions.echo".to_string(),
342 description: Some("Echo function".to_string()),
343 request_format: None,
344 response_format: None,
345 metadata: None,
346 invocation: None,
347 };
348
349 let message = msg.to_message();
350 match &message {
351 Message::RegisterFunction {
352 id, description, ..
353 } => {
354 assert_eq!(id, "functions.echo");
355 assert_eq!(description.as_deref(), Some("Echo function"));
356 }
357 _ => panic!("unexpected message variant"),
358 }
359
360 let serialized = serde_json::to_value(&message).unwrap();
361 assert_eq!(serialized["type"], "registerfunction");
362 assert_eq!(serialized["id"], "functions.echo");
363 assert_eq!(serialized["description"], "Echo function");
364 }
365
366 #[test]
367 fn register_http_function_serializes_invocation() {
368 use super::{HttpInvocationConfig, HttpMethod};
369
370 let msg = RegisterFunctionMessage {
371 id: "external::my_lambda".to_string(),
372 description: None,
373 request_format: None,
374 response_format: None,
375 metadata: None,
376 invocation: Some(HttpInvocationConfig {
377 url: "https://example.com/invoke".to_string(),
378 method: HttpMethod::Post,
379 timeout_ms: Some(30000),
380 headers: HashMap::new(),
381 auth: None,
382 }),
383 };
384
385 let serialized = serde_json::to_value(msg.to_message()).unwrap();
386 assert_eq!(serialized["type"], "registerfunction");
387 assert_eq!(serialized["id"], "external::my_lambda");
388 assert!(serialized["invocation"].is_object());
389 assert_eq!(
390 serialized["invocation"]["url"],
391 "https://example.com/invoke"
392 );
393 assert_eq!(serialized["invocation"]["method"], "POST");
394 }
395}