1use crate::models::authentication::*;
2use crate::models::catalog::*;
3use crate::models::duration::*;
4use crate::models::error::*;
5use crate::models::event::*;
6use crate::models::extension::*;
7use crate::models::input::*;
8use crate::models::map::*;
9use crate::models::output::*;
10use crate::models::retry::*;
11use crate::models::schema::SchemaDefinition;
12use crate::models::task::*;
13use crate::models::timeout::*;
14use serde::{Deserialize, Serialize};
15use serde_json::Value;
16use std::collections::HashMap;
17
18pub const DEFAULT_NAMESPACE: &str = "default";
20fn default_namespace() -> String {
22 DEFAULT_NAMESPACE.to_string()
23}
24
25pub const LATEST_DSL_VERSION: &str = "1.0.3";
27fn default_dsl_version() -> String {
29 LATEST_DSL_VERSION.to_string()
30}
31
32pub const DEFAULT_WORKFLOW_VERSION: &str = "1.0.0";
34fn default_workflow_version() -> String {
35 DEFAULT_WORKFLOW_VERSION.to_string()
36}
37
38fn default_runtime_expression_language() -> String {
40 RuntimeExpressionLanguage::JQ.to_string()
41}
42
43string_constants! {
44 RuntimeExpressionLanguage {
46 JQ => "jq",
47 JAVASCRIPT => "js",
48 }
49}
50
51#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
53pub struct WorkflowDefinition {
54 pub document: WorkflowDefinitionMetadata,
56
57 #[serde(skip_serializing_if = "Option::is_none")]
59 pub input: Option<InputDataModelDefinition>,
60
61 #[serde(rename = "use", skip_serializing_if = "Option::is_none")]
63 pub use_: Option<ComponentDefinitionCollection>,
64
65 #[serde(skip_serializing_if = "Option::is_none")]
67 pub timeout: Option<OneOfTimeoutDefinitionOrReference>,
68
69 #[serde(skip_serializing_if = "Option::is_none")]
71 pub output: Option<OutputDataModelDefinition>,
72
73 #[serde(skip_serializing_if = "Option::is_none")]
75 pub context: Option<ContextDataModelDefinition>,
76
77 #[serde(skip_serializing_if = "Option::is_none")]
79 pub schedule: Option<WorkflowScheduleDefinition>,
80
81 #[serde(skip_serializing_if = "Option::is_none")]
83 pub evaluate: Option<RuntimeExpressionEvaluationConfiguration>,
84
85 #[serde(rename = "do")]
87 pub do_: Map<String, TaskDefinition>,
88
89 #[serde(skip_serializing_if = "Option::is_none")]
91 pub metadata: Option<HashMap<String, Value>>,
92}
93impl WorkflowDefinition {
94 pub fn new(document: WorkflowDefinitionMetadata) -> Self {
96 Self {
97 document,
98 ..Default::default()
99 }
100 }
101}
102
103#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
105pub struct WorkflowDefinitionMetadata {
106 #[serde(default = "default_dsl_version")]
109 pub dsl: String,
110
111 #[serde(default = "default_namespace")]
115 pub namespace: String,
116
117 pub name: String,
119
120 #[serde(default = "default_workflow_version")]
122 pub version: String,
123
124 #[serde(skip_serializing_if = "Option::is_none")]
126 pub title: Option<String>,
127
128 #[serde(skip_serializing_if = "Option::is_none")]
130 pub summary: Option<String>,
131
132 #[serde(skip_serializing_if = "Option::is_none")]
134 pub tags: Option<HashMap<String, String>>,
135
136 #[serde(skip_serializing_if = "Option::is_none")]
138 pub metadata: Option<HashMap<String, Value>>,
139}
140impl WorkflowDefinitionMetadata {
141 pub fn new(
143 namespace: &str,
144 name: &str,
145 version: &str,
146 title: Option<String>,
147 summary: Option<String>,
148 tags: Option<HashMap<String, String>>,
149 ) -> Self {
150 Self {
151 dsl: default_dsl_version(),
152 namespace: namespace.to_owned(),
153 name: name.to_owned(),
154 version: version.to_owned(),
155 title,
156 summary,
157 tags,
158 metadata: None,
159 }
160 }
161}
162
163#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
165pub struct ContextDataModelDefinition {
166 #[serde(skip_serializing_if = "Option::is_none")]
168 pub schema: Option<SchemaDefinition>,
169
170 #[serde(rename = "as", skip_serializing_if = "Option::is_none")]
172 pub as_: Option<serde_json::Value>,
173}
174
175#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
177pub struct WorkflowScheduleDefinition {
178 #[serde(skip_serializing_if = "Option::is_none")]
180 pub every: Option<Duration>,
181
182 #[serde(skip_serializing_if = "Option::is_none")]
184 pub cron: Option<String>,
185
186 #[serde(skip_serializing_if = "Option::is_none")]
188 pub after: Option<Duration>,
189
190 #[serde(skip_serializing_if = "Option::is_none")]
192 pub on: Option<EventConsumptionStrategyDefinition>,
193}
194
195string_constants! {
196 RuntimeExpressionEvaluationMode {
198 STRICT => "strict",
199 LOOSE => "loose",
200 }
201}
202
203string_constants! {
204 RuntimeExpressions {
206 RUNTIME => "runtime",
207 WORKFLOW => "workflow",
208 CONTEXT => "context",
209 ITEM => "item",
210 INDEX => "index",
211 OUTPUT => "output",
212 SECRET => "secret",
213 TASK => "task",
214 INPUT => "input",
215 ERROR => "error",
216 AUTHORIZATION => "authorization",
217 }
218}
219
220#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
222pub struct RuntimeExpressionEvaluationConfiguration {
223 #[serde(default = "default_runtime_expression_language")]
225 pub language: String,
226
227 #[serde(skip_serializing_if = "Option::is_none")]
231 pub mode: Option<String>,
232}
233
234#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
236pub struct ComponentDefinitionCollection {
237 #[serde(skip_serializing_if = "Option::is_none")]
239 pub authentications: Option<HashMap<String, ReferenceableAuthenticationPolicy>>,
240
241 #[serde(skip_serializing_if = "Option::is_none")]
243 pub catalogs: Option<HashMap<String, CatalogDefinition>>,
244
245 #[serde(skip_serializing_if = "Option::is_none")]
247 pub errors: Option<HashMap<String, ErrorDefinition>>,
248
249 #[serde(skip_serializing_if = "Option::is_none")]
251 pub extensions: Option<Vec<HashMap<String, ExtensionDefinition>>>,
252
253 #[serde(skip_serializing_if = "Option::is_none")]
255 pub functions: Option<HashMap<String, TaskDefinition>>,
256
257 #[serde(skip_serializing_if = "Option::is_none")]
259 pub retries: Option<HashMap<String, RetryPolicyDefinition>>,
260
261 #[serde(skip_serializing_if = "Option::is_none")]
263 pub secrets: Option<Vec<String>>,
264
265 #[serde(skip_serializing_if = "Option::is_none")]
267 pub timeouts: Option<HashMap<String, TimeoutDefinition>>,
268}
269
270#[cfg(test)]
271mod tests {
272 use super::*;
273
274 #[test]
275 fn test_workflow_metadata_deserialize() {
276 let json = r#"{
277 "dsl": "1.0.0",
278 "namespace": "test-ns",
279 "name": "test-workflow",
280 "version": "1.0.0",
281 "title": "Test Workflow",
282 "summary": "A test workflow"
283 }"#;
284 let meta: WorkflowDefinitionMetadata = serde_json::from_str(json).unwrap();
285 assert_eq!(meta.dsl, "1.0.0");
286 assert_eq!(meta.namespace, "test-ns");
287 assert_eq!(meta.name, "test-workflow");
288 assert_eq!(meta.version, "1.0.0");
289 assert_eq!(meta.title, Some("Test Workflow".to_string()));
290 }
291
292 #[test]
293 fn test_workflow_metadata_defaults() {
294 let json = r#"{
295 "dsl": "1.0.0",
296 "name": "minimal",
297 "version": "0.1.0"
298 }"#;
299 let meta: WorkflowDefinitionMetadata = serde_json::from_str(json).unwrap();
300 assert_eq!(meta.dsl, "1.0.0");
301 assert_eq!(meta.namespace, "default");
302 }
303
304 #[test]
305 fn test_workflow_metadata_roundtrip() {
306 let json = r#"{
307 "dsl": "1.0.0",
308 "namespace": "test",
309 "name": "myflow",
310 "version": "1.0.0"
311 }"#;
312 let meta: WorkflowDefinitionMetadata = serde_json::from_str(json).unwrap();
313 let serialized = serde_json::to_string(&meta).unwrap();
314 let deserialized: WorkflowDefinitionMetadata = serde_json::from_str(&serialized).unwrap();
315 assert_eq!(meta, deserialized);
316 }
317
318 #[test]
319 fn test_schedule_cron() {
320 let json = r#"{"cron": "0 0 * * *"}"#;
321 let schedule: WorkflowScheduleDefinition = serde_json::from_str(json).unwrap();
322 assert_eq!(schedule.cron, Some("0 0 * * *".to_string()));
323 assert!(schedule.every.is_none());
324 }
325
326 #[test]
327 fn test_schedule_every() {
328 let json = r#"{"every": {"minutes": 30}}"#;
329 let schedule: WorkflowScheduleDefinition = serde_json::from_str(json).unwrap();
330 assert!(schedule.every.is_some());
331 assert!(schedule.cron.is_none());
332 }
333
334 #[test]
335 fn test_schedule_roundtrip() {
336 let json = r#"{"cron": "0 0 * * *"}"#;
337 let schedule: WorkflowScheduleDefinition = serde_json::from_str(json).unwrap();
338 let serialized = serde_json::to_string(&schedule).unwrap();
339 let deserialized: WorkflowScheduleDefinition = serde_json::from_str(&serialized).unwrap();
340 assert_eq!(schedule, deserialized);
341 }
342
343 #[test]
344 fn test_component_collection_deserialize() {
345 let json = r#"{
346 "secrets": ["dbPassword", "apiKey"],
347 "authentications": {
348 "basicAuth": {"basic": {"username": "admin", "password": "secret"}}
349 }
350 }"#;
351 let components: ComponentDefinitionCollection = serde_json::from_str(json).unwrap();
352 assert!(components.secrets.is_some());
353 assert!(components.authentications.is_some());
354 }
355
356 #[test]
357 fn test_component_collection_roundtrip() {
358 let json = r#"{
359 "secrets": ["dbPassword"]
360 }"#;
361 let components: ComponentDefinitionCollection = serde_json::from_str(json).unwrap();
362 let serialized = serde_json::to_string(&components).unwrap();
363 let deserialized: ComponentDefinitionCollection =
364 serde_json::from_str(&serialized).unwrap();
365 assert_eq!(components, deserialized);
366 }
367
368 #[test]
369 fn test_context_data_model_deserialize() {
370 let json = r#"{
371 "schema": {"format": "json", "document": {"type": "object"}},
372 "as": {"sessionId": "abc123"}
373 }"#;
374 let context: ContextDataModelDefinition = serde_json::from_str(json).unwrap();
375 assert!(context.schema.is_some());
376 assert!(context.as_.is_some());
377 }
378
379 #[test]
380 fn test_runtime_expression_config() {
381 let json = r#"{
382 "language": "jq",
383 "mode": "strict"
384 }"#;
385 let config: RuntimeExpressionEvaluationConfiguration = serde_json::from_str(json).unwrap();
386 assert_eq!(config.language, "jq");
387 assert_eq!(config.mode, Some("strict".to_string()));
388 }
389
390 #[test]
391 fn test_runtime_expression_config_defaults() {
392 let json = r#"{}"#;
393 let config: RuntimeExpressionEvaluationConfiguration = serde_json::from_str(json).unwrap();
394 assert_eq!(config.language, "jq");
395 assert!(config.mode.is_none());
396 }
397
398 #[test]
401 fn test_use_definition_comprehensive_deserialize() {
402 let json = r#"{
403 "secrets": ["secret1", "secret2"],
404 "timeouts": {"timeout1": {"after": "PT1M"}}
405 }"#;
406 let use_: ComponentDefinitionCollection = serde_json::from_str(json).unwrap();
407
408 assert!(use_.secrets.is_some());
410 let secrets = use_.secrets.as_ref().unwrap();
411 assert_eq!(secrets.len(), 2);
412 assert!(secrets.contains(&"secret1".to_string()));
413 assert!(secrets.contains(&"secret2".to_string()));
414
415 assert!(use_.timeouts.is_some());
417 let timeouts = use_.timeouts.as_ref().unwrap();
418 assert!(timeouts.contains_key("timeout1"));
419 }
420
421 #[test]
422 fn test_use_definition_minimal() {
423 let json = r#"{
424 "secrets": ["mySecret"]
425 }"#;
426 let use_: ComponentDefinitionCollection = serde_json::from_str(json).unwrap();
427 assert!(use_.secrets.is_some());
428 assert!(use_.authentications.is_none());
429 assert!(use_.errors.is_none());
430 assert!(use_.extensions.is_none());
431 assert!(use_.retries.is_none());
432 assert!(use_.timeouts.is_none());
433 assert!(use_.catalogs.is_none());
434 }
435
436 #[test]
437 fn test_use_definition_empty() {
438 let json = r#"{}"#;
439 let use_: ComponentDefinitionCollection = serde_json::from_str(json).unwrap();
440 assert!(use_.authentications.is_none());
441 assert!(use_.secrets.is_none());
442 }
443
444 #[test]
445 fn test_use_definition_with_catalogs_roundtrip() {
446 let json = r#"{
447 "catalogs": {"default": {"endpoint": "http://example.com/catalog"}}
448 }"#;
449 let use_: ComponentDefinitionCollection = serde_json::from_str(json).unwrap();
450 let serialized = serde_json::to_string(&use_).unwrap();
451 let deserialized: ComponentDefinitionCollection =
452 serde_json::from_str(&serialized).unwrap();
453 assert_eq!(use_, deserialized);
454 }
455
456 #[test]
457 fn test_use_definition_with_timeouts_roundtrip() {
458 let json = r#"{
459 "timeouts": {"short": {"after": "PT10S"}, "long": {"after": "PT1H"}}
460 }"#;
461 let use_: ComponentDefinitionCollection = serde_json::from_str(json).unwrap();
462 let serialized = serde_json::to_string(&use_).unwrap();
463 let deserialized: ComponentDefinitionCollection =
464 serde_json::from_str(&serialized).unwrap();
465 assert_eq!(use_, deserialized);
466 }
467
468 #[test]
469 fn test_document_tags_and_metadata() {
470 let json = r#"{
472 "dsl": "1.0.0",
473 "namespace": "example-namespace",
474 "name": "example-name",
475 "version": "1.0.0",
476 "title": "Example Workflow",
477 "summary": "This is a sample workflow document.",
478 "tags": {"env": "prod", "team": "workflow"},
479 "metadata": {"author": "John Doe", "created": "2025-01-01"}
480 }"#;
481 let meta: WorkflowDefinitionMetadata = serde_json::from_str(json).unwrap();
482 assert_eq!(meta.dsl, "1.0.0");
483 assert_eq!(meta.namespace, "example-namespace");
484 assert_eq!(meta.name, "example-name");
485 assert_eq!(meta.version, "1.0.0");
486 assert_eq!(meta.title, Some("Example Workflow".to_string()));
487 assert_eq!(
488 meta.summary,
489 Some("This is a sample workflow document.".to_string())
490 );
491 assert!(meta.tags.is_some());
492 let tags = meta.tags.as_ref().unwrap();
493 assert_eq!(tags.get("env").map(|s| s.as_str()), Some("prod"));
494 assert_eq!(tags.get("team").map(|s| s.as_str()), Some("workflow"));
495 assert!(meta.metadata.is_some());
496 let md = meta.metadata.as_ref().unwrap();
497 assert!(md.contains_key("author"));
498 }
499
500 #[test]
503 fn test_use_authentications_deserialize() {
504 let json = r#"{"authentications": {"auth1": {"basic": {"username": "alice", "password": "secret"}}}}"#;
505 let use_: ComponentDefinitionCollection = serde_json::from_str(json).unwrap();
506 assert!(use_.authentications.is_some());
507 let auths = use_.authentications.as_ref().unwrap();
508 assert!(auths.contains_key("auth1"));
509 }
510
511 #[test]
512 fn test_use_errors_deserialize() {
513 let json = r#"{"errors": {"error1": {"type": "http://example.com/errors", "title": "Not Found", "status": 404}}}"#;
514 let use_: ComponentDefinitionCollection = serde_json::from_str(json).unwrap();
515 assert!(use_.errors.is_some());
516 let errors = use_.errors.as_ref().unwrap();
517 assert!(errors.contains_key("error1"));
518 }
519
520 #[test]
521 fn test_use_retries_deserialize() {
522 let json = r#"{"retries": {"retry1": {"delay": {"seconds": 5}, "limit": {"attempt": {"count": 3}}}}}"#;
523 let use_: ComponentDefinitionCollection = serde_json::from_str(json).unwrap();
524 assert!(use_.retries.is_some());
525 let retries = use_.retries.as_ref().unwrap();
526 assert!(retries.contains_key("retry1"));
527 }
528
529 #[test]
530 fn test_use_extensions_deserialize() {
531 let json = r#"{"extensions": [{"ext1": {"extend": "call"}}]}"#;
532 let use_: ComponentDefinitionCollection = serde_json::from_str(json).unwrap();
533 assert!(use_.extensions.is_some());
534 }
535
536 #[test]
537 fn test_use_functions_deserialize() {
538 let json = r#"{"functions": {"func1": {"set": {"result": "ok"}}}}"#;
539 let use_: ComponentDefinitionCollection = serde_json::from_str(json).unwrap();
540 assert!(use_.functions.is_some());
541 let funcs = use_.functions.as_ref().unwrap();
542 assert!(funcs.contains_key("func1"));
543 }
544
545 #[test]
546 fn test_use_authentications_roundtrip() {
547 let json = r#"{"authentications": {"auth1": {"basic": {"username": "alice", "password": "secret"}}}}"#;
548 let use_: ComponentDefinitionCollection = serde_json::from_str(json).unwrap();
549 let serialized = serde_json::to_string(&use_).unwrap();
550 let deserialized: ComponentDefinitionCollection =
551 serde_json::from_str(&serialized).unwrap();
552 assert_eq!(use_, deserialized);
553 }
554
555 #[test]
556 fn test_use_errors_roundtrip() {
557 let json = r#"{"errors": {"error1": {"type": "http://example.com/errors", "title": "Not Found", "status": 404}}}"#;
558 let use_: ComponentDefinitionCollection = serde_json::from_str(json).unwrap();
559 let serialized = serde_json::to_string(&use_).unwrap();
560 let deserialized: ComponentDefinitionCollection =
561 serde_json::from_str(&serialized).unwrap();
562 assert_eq!(use_, deserialized);
563 }
564
565 #[test]
566 fn test_use_retries_roundtrip() {
567 let json = r#"{"retries": {"retry1": {"delay": {"seconds": 5}, "limit": {"attempt": {"count": 3}}}}}"#;
568 let use_: ComponentDefinitionCollection = serde_json::from_str(json).unwrap();
569 let serialized = serde_json::to_string(&use_).unwrap();
570 let deserialized: ComponentDefinitionCollection =
571 serde_json::from_str(&serialized).unwrap();
572 assert_eq!(use_, deserialized);
573 }
574
575 #[test]
576 fn test_use_secrets_roundtrip() {
577 let json = r#"{"secrets": ["secret1", "secret2"]}"#;
578 let use_: ComponentDefinitionCollection = serde_json::from_str(json).unwrap();
579 let serialized = serde_json::to_string(&use_).unwrap();
580 let deserialized: ComponentDefinitionCollection =
581 serde_json::from_str(&serialized).unwrap();
582 assert_eq!(use_, deserialized);
583 }
584}