1use noyalib::compat::serde_yaml as serde_yml;
4
5use std::collections::BTreeMap;
6
7use serde::Deserialize;
8
9#[derive(Deserialize, Clone)]
10#[serde(deny_unknown_fields)]
11pub struct RouteDslRoutes {
12 #[serde(default, skip_serializing, rename = "$schema")]
14 pub schema_url: Option<String>,
15
16 #[serde(default)]
17 pub routes: Vec<RouteDslRoute>,
18 #[serde(default)]
19 pub templates: Vec<RouteDslTemplate>,
20 #[serde(default)]
21 pub templated_routes: Vec<RouteDslTemplatedRoute>,
22 #[serde(default)]
23 pub rest: Vec<RouteDslRest>,
24}
25
26#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
27#[derive(Deserialize, Clone)]
28#[serde(deny_unknown_fields)]
29pub struct RouteDslRoute {
30 pub id: String,
31 pub from: String,
32 #[serde(default)]
33 pub steps: Vec<RouteDslStep>,
34 #[serde(default = "default_true")]
35 pub auto_startup: bool,
36 #[serde(default = "default_startup_order")]
37 pub startup_order: i32,
38 #[serde(default)]
39 pub sequential: bool,
40 #[serde(default)]
41 pub concurrent: Option<usize>,
42 #[serde(default)]
43 pub error_handler: Option<RouteDslErrorHandler>,
44 #[serde(default)]
45 pub circuit_breaker: Option<RouteDslCircuitBreaker>,
46 #[serde(default)]
47 pub security_policy: Option<RouteDslSecurityPolicy>,
48 #[serde(default)]
49 pub on_complete: Option<String>,
50 #[serde(default)]
51 pub on_failure: Option<String>,
52}
53
54#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
55#[derive(Deserialize, Debug, Clone)]
56#[serde(deny_unknown_fields)]
57pub struct RouteDslSecurityPolicy {
58 #[serde(default)]
59 pub roles: Option<Vec<String>>,
60 #[serde(default)]
61 pub scopes: Option<Vec<String>>,
62 #[serde(default)]
63 pub all_required: Option<bool>,
64 #[serde(default)]
70 pub trust_upstream_principal: Option<bool>,
71 #[serde(default)]
72 pub r#ref: Option<String>,
73 #[serde(default)]
74 pub wasm: Option<String>,
75 #[serde(default)]
76 pub config: Option<std::collections::HashMap<String, String>>,
77 #[serde(default)]
78 pub permission: Option<RouteDslPermissionPolicy>,
79}
80
81#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
82#[derive(Deserialize, Debug, Clone)]
83#[serde(deny_unknown_fields)]
84pub struct RouteDslPermissionPolicy {
85 pub policy: String,
86 #[serde(default)]
87 pub resource: Option<RouteDslPermissionValueSource>,
88 #[serde(default)]
89 pub action: Option<RouteDslPermissionValueSource>,
90 #[serde(default)]
91 pub scopes: Option<Vec<String>>,
92 #[serde(default)]
93 pub context: Option<RouteDslPermissionContext>,
94 #[serde(default)]
95 pub cache_ttl_secs: Option<u64>,
96 #[serde(default)]
97 pub cache_negative_ttl_secs: Option<u64>,
98}
99
100#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
101#[derive(Deserialize, Debug, Clone)]
102#[serde(deny_unknown_fields)]
103pub struct RouteDslPermissionValueSource {
104 #[serde(default)]
105 pub literal: Option<String>,
106 #[serde(default)]
107 pub header: Option<String>,
108 #[serde(default)]
109 pub property: Option<String>,
110}
111
112#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
113#[derive(Deserialize, Debug, Clone)]
114#[serde(deny_unknown_fields)]
115pub struct RouteDslPermissionContext {
116 #[serde(default)]
117 pub headers: Vec<String>,
118 #[serde(default)]
119 pub properties: Vec<String>,
120}
121
122#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
123#[derive(Deserialize, Clone)]
124#[serde(deny_unknown_fields)]
125pub struct RouteDslErrorHandler {
126 #[serde(default)]
127 pub dead_letter_channel: Option<String>,
128 #[serde(default)]
129 pub retry: Option<RouteDslRedeliveryPolicy>,
130 #[serde(default)]
131 pub on_exceptions: Option<Vec<RouteDslOnException>>,
132 #[serde(default)]
133 pub use_original_message: bool,
134}
135
136#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
137#[derive(Deserialize, Clone)]
138#[serde(deny_unknown_fields)]
139pub struct RouteDslOnException {
140 #[serde(default)]
141 pub kind: Option<String>,
142 #[serde(default)]
143 pub message_contains: Option<String>,
144 #[serde(default)]
145 pub retry: Option<RouteDslRedeliveryPolicy>,
146 #[serde(default)]
147 pub steps: Vec<RouteDslStep>,
148 #[serde(default)]
149 pub handled: Option<bool>,
150 #[serde(default)]
151 pub continued: Option<bool>,
152}
153
154#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
155#[derive(Deserialize, Clone)]
156#[serde(deny_unknown_fields)]
157pub struct RouteDslRedeliveryPolicy {
158 pub max_attempts: u32,
159 #[serde(default = "default_initial_delay_ms")]
160 pub initial_delay_ms: u64,
161 #[serde(default = "default_multiplier")]
162 pub multiplier: f64,
163 #[serde(default = "default_max_delay_ms")]
164 pub max_delay_ms: u64,
165 #[serde(default = "default_jitter_factor")]
166 pub jitter_factor: f64,
167 #[serde(default)]
168 pub handled_by: Option<String>,
169}
170
171#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
172#[derive(Deserialize, Clone)]
173#[serde(deny_unknown_fields)]
174pub struct RouteDslCircuitBreaker {
175 #[serde(default = "default_failure_threshold")]
176 pub failure_threshold: u32,
177 #[serde(default = "default_open_duration_ms")]
178 pub open_duration_ms: u64,
179}
180
181fn default_true() -> bool {
182 true
183}
184
185fn default_startup_order() -> i32 {
186 1000
187}
188
189fn default_initial_delay_ms() -> u64 {
190 100
191}
192
193fn default_multiplier() -> f64 {
194 2.0
195}
196
197fn default_max_delay_ms() -> u64 {
198 10_000
199}
200
201fn default_jitter_factor() -> f64 {
202 0.0
203}
204
205fn default_failure_threshold() -> u32 {
206 5
207}
208
209fn default_open_duration_ms() -> u64 {
210 30_000
211}
212
213#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
214#[derive(Deserialize, Debug, Clone)]
215#[serde(deny_unknown_fields)]
216pub struct DelayStep {
217 pub delay: DelayBody,
218}
219
220#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
221#[derive(Deserialize, Debug, Clone)]
222#[serde(untagged)]
223#[cfg_attr(feature = "schema", schemars(untagged))]
224pub enum DelayBody {
225 Short(u64),
226 Full(DelayFullConfig),
227}
228
229#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
230#[derive(Deserialize, Debug, Clone)]
231#[serde(deny_unknown_fields)]
232pub struct DelayFullConfig {
233 pub delay_ms: u64,
234 #[serde(default)]
235 pub dynamic_header: Option<String>,
236}
237
238#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
239#[derive(Deserialize, Debug, Clone)]
240#[serde(deny_unknown_fields)]
241pub struct LoopStep {
242 #[serde(rename = "loop")]
243 pub loop_data: LoopData,
244}
245
246#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
247#[derive(Deserialize, Debug, Clone)]
248#[serde(untagged)]
249#[cfg_attr(feature = "schema", schemars(untagged))]
250pub enum LoopData {
251 Count(usize),
253 Full(LoopFullConfig),
255}
256
257#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
258#[derive(Deserialize, Debug, Clone)]
259#[serde(deny_unknown_fields)]
260pub struct LoopFullConfig {
261 pub count: Option<usize>,
263 #[serde(rename = "while")]
265 pub while_expr: Option<LoopWhileExpr>,
266 #[serde(default)]
267 pub steps: Vec<RouteDslStep>,
268 #[serde(default)]
269 pub max_iterations: Option<usize>,
270}
271
272#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
274#[derive(Deserialize, Debug, Clone)]
275#[serde(deny_unknown_fields)]
276pub struct LoopWhileExpr {
277 #[serde(default)]
278 pub language: Option<String>,
279 #[serde(default)]
280 pub source: Option<String>,
281 #[serde(default)]
282 pub simple: Option<String>,
283 #[serde(default)]
284 pub rhai: Option<String>,
285 #[serde(default)]
286 pub jsonpath: Option<String>,
287 #[serde(default)]
288 pub xpath: Option<String>,
289}
290
291#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
292#[derive(Deserialize, Debug, Clone)]
293#[serde(untagged)]
294#[cfg_attr(feature = "schema", schemars(untagged))]
295pub enum RouteDslStep {
296 To(ToStep),
297 SetHeader(SetHeaderStep),
298 #[serde(skip_deserializing)]
299 #[cfg_attr(feature = "schema", schemars(skip))]
300 #[cfg_attr(feature = "schema", ts(skip))]
301 SetHeaderIfAbsent(SetHeaderStep),
302 SetProperty(SetPropertyStep),
303 SetBody(SetBodyStep),
304 Bean(BeanStep),
305 Choice(ChoiceStep),
306 DynamicRouter(DynamicRouterStep),
307 Filter(FilterStep),
308 Function(FunctionStep),
309 LoadBalance(LoadBalanceStep),
310 Log(LogStep),
311 Split(SplitStep),
312 Aggregate(AggregateStep),
313 WireTap(WireTapStep),
314 Multicast(MulticastStep),
315 RoutingSlip(RoutingSlipStep),
316 RecipientList(RecipientListStep),
317 ScatterGather(ScatterGatherStep),
318 Stop(StopStep),
319 StreamCache(StreamCacheStep),
320 Throttle(ThrottleStep),
321 Transform(TransformStep),
322 Script(ScriptStep),
323 ConvertBodyTo(ConvertBodyToStep),
324 Marshal(MarshalStep),
325 Unmarshal(UnmarshalStep),
326 Delay(DelayStep),
327 DoTry(DoTryStep),
328 Loop(LoopStep),
329 Validate(ValidateStep),
330 Enrich(EnrichStep),
331 PollEnrich(PollEnrichStep),
332 IdempotentConsumer(IdempotentConsumerStep),
333 ClaimCheck(ClaimCheckStep),
334 Sampling(SamplingStep),
335 Sort(SortStep),
336 Resequence(ResequenceStep),
337}
338
339#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
340#[derive(Deserialize, Debug, Clone)]
341#[serde(deny_unknown_fields)]
342pub struct FunctionStep {
343 pub function: FunctionData,
344}
345
346#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
347#[derive(Deserialize, Debug, Clone)]
348#[serde(deny_unknown_fields)]
349pub struct FunctionData {
350 pub runtime: String,
351 pub source: String,
352 #[serde(default)]
353 pub timeout_ms: Option<u64>,
354}
355
356#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
357#[derive(Deserialize, Debug, Clone)]
358#[serde(deny_unknown_fields)]
359pub struct ToStep {
360 pub to: String,
361}
362
363#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
364#[derive(Deserialize, Debug, Clone)]
365#[serde(deny_unknown_fields)]
366pub struct SetHeaderStep {
367 pub set_header: SetHeaderData,
368}
369
370#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
371#[derive(Deserialize, Debug, Clone)]
372#[serde(deny_unknown_fields)]
373pub struct SetPropertyStep {
374 pub set_property: SetPropertyData,
375}
376
377#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
378#[derive(Deserialize, Debug, Clone)]
379#[serde(deny_unknown_fields)]
380pub struct SetHeaderData {
381 pub key: String,
382 #[serde(default)]
383 pub value: Option<serde_json::Value>,
384 #[serde(default)]
385 pub language: Option<String>,
386 #[serde(default)]
387 pub source: Option<String>,
388 #[serde(default)]
389 pub simple: Option<String>,
390 #[serde(default)]
391 pub rhai: Option<String>,
392 #[serde(default)]
393 pub jsonpath: Option<String>,
394 #[serde(default)]
395 pub xpath: Option<String>,
396}
397
398#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
399#[derive(Deserialize, Debug, Clone)]
400#[serde(deny_unknown_fields)]
401pub struct SetPropertyData {
402 pub name: String,
403 #[serde(default)]
404 pub value: Option<serde_json::Value>,
405 #[serde(default)]
406 pub language: Option<String>,
407 #[serde(default)]
408 pub source: Option<String>,
409 #[serde(default)]
410 pub simple: Option<String>,
411 #[serde(default)]
412 pub rhai: Option<String>,
413 #[serde(default)]
414 pub jsonpath: Option<String>,
415 #[serde(default)]
416 pub xpath: Option<String>,
417}
418
419#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
420#[derive(Deserialize, Debug, Clone)]
421#[serde(deny_unknown_fields)]
422pub struct SetBodyStep {
423 pub set_body: SetBodyData,
424}
425
426#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
428#[derive(Deserialize, Debug, Clone)]
429#[serde(deny_unknown_fields)]
430pub struct TransformStep {
431 pub transform: SetBodyData,
432}
433
434#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
435#[derive(Deserialize, Debug, Clone)]
436#[serde(untagged)]
437#[cfg_attr(feature = "schema", schemars(untagged))]
438pub enum SetBodyData {
439 Config(SetBodyConfig),
444 Literal(serde_json::Value),
445}
446
447#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
448#[derive(Deserialize, Debug, Clone)]
449#[serde(deny_unknown_fields)]
450pub struct SetBodyConfig {
451 #[serde(default)]
452 pub value: Option<serde_json::Value>,
453 #[serde(default)]
454 pub language: Option<String>,
455 #[serde(default)]
456 pub source: Option<String>,
457 #[serde(default)]
458 pub simple: Option<String>,
459 #[serde(default)]
460 pub rhai: Option<String>,
461 #[serde(default)]
462 pub jsonpath: Option<String>,
463 #[serde(default)]
464 pub xpath: Option<String>,
465}
466
467#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
468#[derive(Deserialize, Debug, Clone)]
469#[serde(untagged)]
470#[cfg_attr(feature = "schema", schemars(untagged))]
471pub enum LogBody {
472 Message(String),
473 Config(LogConfig),
474}
475
476#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
477#[derive(Deserialize, Debug, Clone)]
478#[serde(deny_unknown_fields)]
479pub struct LogConfig {
480 pub message: LogMessageData,
482 #[serde(default)]
483 pub level: Option<String>,
484}
485
486#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
489#[derive(Deserialize, Debug, Clone)]
490#[serde(untagged)]
491#[cfg_attr(feature = "schema", schemars(untagged))]
492pub enum LogMessageData {
493 Literal(String),
494 Expr(LogMessageExpr),
495}
496
497#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
498#[derive(Deserialize, Debug, Clone)]
499#[serde(deny_unknown_fields)]
500pub struct LogMessageExpr {
501 #[serde(default)]
502 pub value: Option<String>,
503 #[serde(default)]
504 pub language: Option<String>,
505 #[serde(default)]
506 pub source: Option<String>,
507 #[serde(default)]
508 pub simple: Option<String>,
509 #[serde(default)]
510 pub rhai: Option<String>,
511 #[serde(default)]
512 pub jsonpath: Option<String>,
513 #[serde(default)]
514 pub xpath: Option<String>,
515}
516
517#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
518#[derive(Deserialize, Debug, Clone)]
519#[serde(deny_unknown_fields)]
520pub struct LogStep {
521 pub log: LogBody,
522}
523
524#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
525#[derive(Deserialize, Debug, Clone)]
526#[serde(deny_unknown_fields)]
527pub struct FilterStep {
528 pub filter: PredicateBlock,
529}
530
531#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
532#[derive(Deserialize, Debug, Clone)]
533#[serde(deny_unknown_fields)]
534pub struct PredicateBlock {
535 #[serde(default)]
536 pub language: Option<String>,
537 #[serde(default)]
538 pub source: Option<String>,
539 #[serde(default)]
540 pub simple: Option<String>,
541 #[serde(default)]
542 pub rhai: Option<String>,
543 #[serde(default)]
544 pub jsonpath: Option<String>,
545 #[serde(default)]
546 pub xpath: Option<String>,
547 #[serde(default)]
548 pub steps: Vec<RouteDslStep>,
549}
550
551#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
552#[derive(Deserialize, Debug, Clone)]
553#[serde(deny_unknown_fields)]
554pub struct ChoiceStep {
555 pub choice: ChoiceData,
556}
557
558#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
559#[derive(Deserialize, Debug, Clone)]
560#[serde(deny_unknown_fields)]
561pub struct ChoiceData {
562 #[serde(default)]
563 pub when: Vec<PredicateBlock>,
564 #[serde(default)]
565 pub otherwise: Option<Vec<RouteDslStep>>,
566}
567
568#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
569#[derive(Deserialize, Debug, Clone)]
570#[serde(deny_unknown_fields)]
571pub struct DoTryStep {
572 pub do_try: DoTryData,
573}
574
575#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
576#[derive(Deserialize, Debug, Clone)]
577#[serde(deny_unknown_fields)]
578pub struct DoTryData {
579 pub steps: Vec<RouteDslStep>,
580 #[serde(default)]
581 pub catch: Vec<CatchClauseData>,
582 #[serde(default)]
583 pub finally: Option<FinallyData>,
584}
585
586#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
587#[derive(Deserialize, Debug, Clone)]
588#[serde(deny_unknown_fields)]
589pub struct CatchClauseData {
590 pub exception: Option<Vec<String>>,
591 pub when: Option<String>,
592 pub on_when: Option<String>,
593 #[serde(default = "default_handled_disposition")]
594 pub disposition: camel_api::error_handler::ExceptionDisposition,
595 pub steps: Vec<RouteDslStep>,
596}
597
598#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
599#[derive(Deserialize, Debug, Clone)]
600#[serde(deny_unknown_fields)]
601pub struct FinallyData {
602 pub on_when: Option<String>,
603 pub steps: Vec<RouteDslStep>,
604}
605
606fn default_handled_disposition() -> camel_api::error_handler::ExceptionDisposition {
607 camel_api::error_handler::ExceptionDisposition::Handled
608}
609
610#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
611#[derive(Deserialize, Debug, Clone)]
612#[serde(deny_unknown_fields)]
613pub struct SplitStep {
614 pub split: SplitData,
615}
616
617#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
618#[derive(Deserialize, Debug, Clone)]
619#[serde(deny_unknown_fields)]
620pub struct SplitData {
621 #[serde(default)]
622 pub expression: Option<SplitExpressionYaml>,
623 #[serde(default = "default_split_aggregation")]
624 pub aggregation: String,
625 #[serde(default)]
626 pub parallel: bool,
627 #[serde(default)]
628 pub parallel_limit: Option<usize>,
629 #[serde(default = "default_true")]
630 pub stop_on_exception: bool,
631 #[serde(default)]
632 pub steps: Vec<RouteDslStep>,
633 #[serde(default)]
634 pub streaming: bool,
635 #[serde(default)]
636 pub stream: Option<StreamConfigYaml>,
637}
638
639#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
640#[derive(Default, Deserialize, Debug, Clone)]
641#[serde(deny_unknown_fields)]
642pub struct StreamConfigYaml {
643 #[serde(default)]
644 pub format: Option<String>,
645 #[serde(default)]
646 pub max_record_bytes: Option<usize>,
647 #[serde(default)]
648 pub batch_size: Option<usize>,
649 #[serde(default)]
650 pub chunk_size: Option<usize>,
651}
652
653#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
654#[derive(Deserialize, Debug, Clone)]
655#[serde(untagged)]
656#[cfg_attr(feature = "schema", schemars(untagged))]
657pub enum SplitExpressionYaml {
658 Simple(String),
659 Config(SplitExpressionConfig),
660}
661
662#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
663#[derive(Deserialize, Debug, Clone)]
664#[serde(deny_unknown_fields)]
665pub struct SplitExpressionConfig {
666 #[serde(default)]
667 pub language: Option<String>,
668 #[serde(default)]
669 pub source: Option<String>,
670 #[serde(default)]
671 pub simple: Option<String>,
672 #[serde(default)]
673 pub rhai: Option<String>,
674 #[serde(default)]
675 pub jsonpath: Option<String>,
676 #[serde(default)]
677 pub xpath: Option<String>,
678}
679
680fn default_split_aggregation() -> String {
681 "last_wins".to_string()
682}
683
684#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
685#[derive(Deserialize, Debug, Clone)]
686#[serde(deny_unknown_fields)]
687pub struct AggregateStep {
688 pub aggregate: AggregateData,
689}
690
691#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
692#[derive(Deserialize, Debug, Clone)]
693#[serde(deny_unknown_fields)]
694pub struct AggregateData {
695 pub header: String,
696 #[serde(default)]
697 pub correlation_key: Option<String>,
698 #[serde(default)]
699 pub completion_size: Option<usize>,
700 #[serde(default)]
701 pub completion_timeout_ms: Option<u64>,
702 #[serde(default)]
703 pub completion_predicate: Option<PredicateBlock>,
704 #[serde(default = "default_aggregate_strategy")]
705 pub strategy: String,
706 #[serde(default)]
707 pub max_buckets: Option<usize>,
708 #[serde(default)]
709 pub bucket_ttl_ms: Option<u64>,
710 #[serde(default)]
711 pub force_completion_on_stop: Option<bool>,
712 #[serde(default)]
713 pub discard_on_timeout: Option<bool>,
714}
715
716fn default_aggregate_strategy() -> String {
717 "collect_all".to_string()
718}
719
720#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
721#[derive(Deserialize, Debug, Clone)]
722#[serde(deny_unknown_fields)]
723pub struct WireTapStep {
724 pub wire_tap: String,
725}
726
727#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
728#[derive(Deserialize, Debug, Clone)]
729#[serde(deny_unknown_fields)]
730pub struct MulticastStep {
731 pub multicast: MulticastData,
732}
733
734#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
735#[derive(Deserialize, Debug, Clone)]
736#[serde(deny_unknown_fields)]
737pub struct MulticastData {
738 #[serde(default)]
739 pub parallel: bool,
740 #[serde(default)]
741 pub parallel_limit: Option<usize>,
742 #[serde(default)]
743 pub stop_on_exception: bool,
744 #[serde(default)]
745 pub timeout_ms: Option<u64>,
746 #[serde(default = "default_multicast_aggregation")]
747 pub aggregation: String,
748 #[serde(default)]
749 pub steps: Vec<RouteDslStep>,
750}
751
752fn default_multicast_aggregation() -> String {
753 "last_wins".to_string()
754}
755
756#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
757#[derive(Deserialize, Debug, Clone)]
758#[serde(deny_unknown_fields)]
759pub struct ScatterGatherStep {
760 pub scatter_gather: ScatterGatherData,
761}
762
763#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
768#[derive(Deserialize, Debug, Clone)]
769#[serde(deny_unknown_fields)]
770pub struct ScatterGatherData {
771 #[serde(default)]
772 pub endpoints: Vec<String>,
773 #[serde(default = "default_multicast_aggregation")]
774 pub aggregation: String,
775}
776
777#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
778#[derive(Deserialize, Debug, Clone)]
779#[serde(deny_unknown_fields)]
780pub struct StopStep {
781 pub stop: bool,
782}
783
784#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
785#[derive(Deserialize, Debug, Clone)]
786#[serde(deny_unknown_fields)]
787pub struct StreamCacheStep {
788 pub stream_cache: StreamCacheBody,
789}
790
791#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
792#[derive(Deserialize, Debug, Clone)]
793#[serde(untagged)]
794#[cfg_attr(feature = "schema", schemars(untagged))]
795pub enum StreamCacheBody {
796 Enabled(bool),
797 Config(StreamCacheConfig),
798}
799
800#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
801#[derive(Deserialize, Debug, Clone)]
802#[serde(deny_unknown_fields)]
803pub struct StreamCacheConfig {
804 #[serde(default)]
805 pub threshold: Option<usize>,
806}
807
808#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
809#[derive(Deserialize, Debug, Clone)]
810#[serde(deny_unknown_fields)]
811pub struct ScriptStep {
812 pub script: ScriptData,
813}
814
815#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
816#[derive(Deserialize, Debug, Clone)]
817#[serde(deny_unknown_fields)]
818pub struct ConvertBodyToStep {
819 pub convert_body_to: String,
820}
821
822#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
823#[derive(Deserialize, Debug, Clone)]
824#[serde(deny_unknown_fields)]
825pub struct MarshalStep {
826 pub marshal: String,
827 #[serde(default)]
828 pub config: Option<serde_json::Value>,
829}
830
831#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
832#[derive(Deserialize, Debug, Clone)]
833#[serde(deny_unknown_fields)]
834pub struct UnmarshalStep {
835 pub unmarshal: String,
836 #[serde(default)]
842 pub schema: Option<serde_json::Value>,
843 #[serde(default)]
844 pub config: Option<serde_json::Value>,
845}
846
847#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
848#[derive(Deserialize, Debug, Clone)]
849#[serde(deny_unknown_fields)]
850pub struct ValidateStep {
851 pub validate: String,
852}
853
854#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
855#[derive(Deserialize, Debug, Clone)]
856#[serde(deny_unknown_fields)]
857pub struct ClaimCheckStep {
858 pub claim_check: ClaimCheckBody,
859}
860
861#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
862#[derive(Deserialize, Debug, Clone)]
863#[serde(deny_unknown_fields)]
864pub struct ClaimCheckBody {
865 pub repository: String,
867 pub operation: String,
869 pub key: String,
871 #[serde(default)]
874 pub filter: Option<String>,
875}
876
877#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
878#[derive(Deserialize, Debug, Clone)]
879#[serde(deny_unknown_fields)]
880pub struct SamplingStep {
881 pub sampling: SamplingBody,
882}
883
884#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
885#[derive(Deserialize, Debug, Clone)]
886#[serde(untagged)]
887#[cfg_attr(feature = "schema", schemars(untagged))]
888pub enum SamplingBody {
889 Short(usize),
891 Full(SamplingConfig),
893}
894
895#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
896#[derive(Deserialize, Debug, Clone)]
897#[serde(deny_unknown_fields)]
898pub struct SamplingConfig {
899 pub period: usize,
900}
901
902#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
903#[derive(Deserialize, Debug, Clone)]
904#[serde(deny_unknown_fields)]
905pub struct SortStep {
906 pub sort: SortBody,
907}
908
909#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
910#[derive(Deserialize, Debug, Clone)]
911#[serde(deny_unknown_fields)]
912pub struct SortBody {
913 pub expression: String,
915 #[serde(default)]
917 pub reverse: bool,
918 #[serde(default)]
920 pub language: Option<String>,
921}
922
923#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
924#[derive(Deserialize, Debug, Clone)]
925#[serde(deny_unknown_fields)]
926pub struct IdempotentConsumerStep {
927 pub idempotent_consumer: IdempotentConsumerBody,
928}
929
930#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
931#[derive(Deserialize, Debug, Clone)]
932#[serde(deny_unknown_fields)]
933pub struct IdempotentConsumerBody {
934 pub repository: String,
936 pub expression: String,
938 #[serde(default)]
940 pub steps: Vec<RouteDslStep>,
941 #[serde(default)]
943 pub eager: Option<bool>,
944 #[serde(default)]
946 pub remove_on_failure: Option<bool>,
947}
948
949#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
950#[derive(Deserialize, Debug, Clone)]
951#[serde(deny_unknown_fields)]
952pub struct EnrichStep {
953 pub enrich: EnrichBody,
954}
955
956#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
957#[derive(Deserialize, Debug, Clone)]
958#[serde(deny_unknown_fields)]
959pub struct PollEnrichStep {
960 pub poll_enrich: EnrichBody,
961}
962
963#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
964#[derive(Deserialize, Debug, Clone)]
965#[serde(untagged)]
966#[cfg_attr(feature = "schema", schemars(untagged))]
967pub enum EnrichBody {
968 Uri(String),
970 Full(EnrichConfig),
972}
973
974#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
975#[derive(Deserialize, Debug, Clone)]
976#[serde(deny_unknown_fields)]
977pub struct EnrichConfig {
978 pub uri: String,
979 #[serde(default)]
980 pub strategy: Option<String>,
981 #[serde(default)]
982 pub timeout: Option<u64>,
983}
984
985#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
986#[derive(Deserialize, Debug, Clone)]
987#[serde(deny_unknown_fields)]
988pub struct ScriptData {
989 pub language: String,
990 pub source: String,
991}
992
993#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
994#[derive(Deserialize, Debug, Clone)]
995#[serde(deny_unknown_fields)]
996pub struct ThrottleStep {
997 pub throttle: ThrottleData,
998}
999
1000#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
1001#[derive(Deserialize, Debug, Clone)]
1002#[serde(deny_unknown_fields)]
1003pub struct ThrottleData {
1004 pub max_requests: usize,
1005 #[serde(default = "default_throttle_period_secs")]
1006 pub period_secs: u64,
1007 #[serde(default)]
1008 pub strategy: Option<String>,
1009 #[serde(default)]
1010 pub steps: Vec<RouteDslStep>,
1011}
1012
1013fn default_throttle_period_secs() -> u64 {
1014 1
1015}
1016
1017#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
1018#[derive(Deserialize, Debug, Clone)]
1019#[serde(deny_unknown_fields)]
1020pub struct LoadBalanceStep {
1021 pub load_balance: LoadBalanceData,
1022}
1023
1024#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
1025#[derive(Deserialize, Debug, Clone)]
1026#[serde(deny_unknown_fields)]
1027pub struct LoadBalanceData {
1028 #[serde(default = "default_lb_strategy")]
1029 pub strategy: String,
1030 #[serde(default)]
1031 pub distribution_ratio: Option<String>,
1032 #[serde(default)]
1033 pub steps: Vec<RouteDslStep>,
1034}
1035
1036fn default_lb_strategy() -> String {
1037 "round_robin".to_string()
1038}
1039
1040#[derive(Deserialize, Debug, Clone)]
1046#[serde(deny_unknown_fields)]
1047pub struct RouteDslTemplate {
1048 pub id: String,
1049 #[serde(default)]
1050 pub parameters: Vec<RouteDslTemplateParameter>,
1051 #[serde(default)]
1052 pub routes: Vec<serde_yml::Value>,
1053}
1054
1055#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
1057#[derive(Deserialize, Debug, Clone)]
1058#[serde(deny_unknown_fields)]
1059pub struct RouteDslTemplateParameter {
1060 pub name: String,
1062 #[serde(default)]
1064 pub default_value: Option<String>,
1065 #[serde(default)]
1067 pub description: Option<String>,
1068}
1069
1070#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
1072#[derive(Deserialize, Debug, Clone)]
1073#[serde(deny_unknown_fields)]
1074pub struct RouteDslTemplatedRoute {
1075 pub route_template_ref: String,
1076 #[serde(default)]
1078 pub route_id: Option<String>,
1079 #[serde(default)]
1081 pub parameters: BTreeMap<String, String>,
1082}
1083
1084#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
1090#[derive(Deserialize, Debug, Clone)]
1091#[serde(deny_unknown_fields)]
1092pub struct RouteDslRest {
1093 #[serde(default = "default_rest_host")]
1095 pub host: String,
1096 #[serde(default = "default_rest_port")]
1098 pub port: u16,
1099 #[serde(default)]
1101 pub path: String,
1102 #[serde(default)]
1104 pub operations: Vec<RouteDslRestOperation>,
1105}
1106
1107#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
1109#[derive(Deserialize, Debug, Clone)]
1110#[serde(deny_unknown_fields)]
1111pub struct RouteDslRestOperation {
1112 pub method: String,
1115 #[serde(default = "default_operation_path")]
1117 pub path: String,
1118 #[serde(default)]
1120 pub operation_id: Option<String>,
1121 #[serde(default)]
1123 pub to: Option<String>,
1124 #[serde(default)]
1126 pub steps: Vec<RouteDslStep>,
1127 #[serde(default = "default_consumes")]
1129 pub consumes: String,
1130 #[serde(default = "default_produces")]
1132 pub produces: String,
1133 #[serde(default)]
1135 pub success_status: Option<u16>,
1136 #[serde(default)]
1138 pub request_schema: Option<serde_json::Value>,
1139 #[serde(default)]
1141 pub response: Option<RouteDslRestResponse>,
1142 #[serde(default)]
1144 pub description: Option<String>,
1145 #[serde(default)]
1147 pub parameters: BTreeMap<String, serde_json::Value>,
1148}
1149
1150#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
1152#[derive(Deserialize, Debug, Clone)]
1153#[serde(deny_unknown_fields)]
1154pub struct RouteDslRestResponse {
1155 #[serde(default)]
1157 pub description: Option<String>,
1158 #[serde(default)]
1160 pub schema: Option<serde_json::Value>,
1161 #[serde(default)]
1163 pub headers: BTreeMap<String, serde_json::Value>,
1164}
1165
1166fn default_rest_host() -> String {
1167 "0.0.0.0".to_string()
1168}
1169
1170fn default_rest_port() -> u16 {
1171 8080
1172}
1173
1174fn default_operation_path() -> String {
1175 "/".to_string()
1176}
1177
1178fn default_consumes() -> String {
1179 "application/json".to_string()
1180}
1181
1182fn default_produces() -> String {
1183 "application/json".to_string()
1184}
1185
1186#[cfg(test)]
1187mod tests {
1188 use super::*;
1189
1190 #[test]
1191 fn default_values() {
1192 assert!(default_true());
1193 assert_eq!(default_startup_order(), 1000);
1194 assert_eq!(default_initial_delay_ms(), 100);
1195 assert_eq!(default_multiplier(), 2.0);
1196 assert_eq!(default_max_delay_ms(), 10_000);
1197 assert_eq!(default_jitter_factor(), 0.0);
1198 assert_eq!(default_failure_threshold(), 5);
1199 assert_eq!(default_open_duration_ms(), 30_000);
1200 assert_eq!(default_split_aggregation(), "last_wins");
1201 assert_eq!(default_aggregate_strategy(), "collect_all");
1202 assert_eq!(default_multicast_aggregation(), "last_wins");
1203 assert_eq!(default_throttle_period_secs(), 1);
1204 assert_eq!(default_lb_strategy(), "round_robin");
1205 assert_eq!(default_uri_delimiter(), ",");
1206 assert_eq!(default_cache_size(), 1000);
1207 assert_eq!(default_max_iterations(), 1000);
1208 assert_eq!(default_rest_host(), "0.0.0.0");
1209 assert_eq!(default_rest_port(), 8080);
1210 assert_eq!(default_operation_path(), "/");
1211 assert_eq!(default_consumes(), "application/json");
1212 assert_eq!(default_produces(), "application/json");
1213 }
1214
1215 #[test]
1216 fn parse_minimal_route() {
1217 let yaml = r#"
1218routes:
1219 - id: r1
1220 from: direct:start
1221"#;
1222 let parsed: RouteDslRoutes = serde_yml::from_str(yaml).unwrap();
1223 assert_eq!(parsed.routes.len(), 1);
1224 assert_eq!(parsed.routes[0].id, "r1");
1225 assert_eq!(parsed.routes[0].from, "direct:start");
1226 assert!(parsed.routes[0].auto_startup);
1227 assert_eq!(parsed.routes[0].startup_order, 1000);
1228 assert!(parsed.routes[0].steps.is_empty());
1229 }
1230
1231 #[test]
1232 fn parse_route_with_concurrency() {
1233 let yaml = r#"
1234routes:
1235 - id: r2
1236 from: timer:tick
1237 concurrent: 4
1238"#;
1239 let parsed: RouteDslRoutes = serde_yml::from_str(yaml).unwrap();
1240 assert_eq!(parsed.routes[0].concurrent, Some(4));
1241 assert!(!parsed.routes[0].sequential);
1242 }
1243
1244 #[test]
1245 fn parse_route_with_error_handler() {
1246 let yaml = r#"
1247routes:
1248 - id: r3
1249 from: direct:in
1250 error_handler:
1251 dead_letter_channel: log:dlq
1252"#;
1253 let parsed: RouteDslRoutes = serde_yml::from_str(yaml).unwrap();
1254 let eh = parsed.routes[0].error_handler.as_ref().unwrap();
1255 assert_eq!(eh.dead_letter_channel.as_deref(), Some("log:dlq"));
1256 }
1257
1258 #[test]
1259 fn parse_route_with_circuit_breaker() {
1260 let yaml = r#"
1261routes:
1262 - id: r4
1263 from: direct:in
1264 circuit_breaker:
1265 failure_threshold: 3
1266 open_duration_ms: 5000
1267"#;
1268 let parsed: RouteDslRoutes = serde_yml::from_str(yaml).unwrap();
1269 let cb = parsed.routes[0].circuit_breaker.as_ref().unwrap();
1270 assert_eq!(cb.failure_threshold, 3);
1271 assert_eq!(cb.open_duration_ms, 5000);
1272 }
1273
1274 #[test]
1275 fn parse_circuit_breaker_defaults() {
1276 let yaml = r#"
1277routes:
1278 - id: r5
1279 from: direct:in
1280 circuit_breaker: {}
1281"#;
1282 let parsed: RouteDslRoutes = serde_yml::from_str(yaml).unwrap();
1283 let cb = parsed.routes[0].circuit_breaker.as_ref().unwrap();
1284 assert_eq!(cb.failure_threshold, 5);
1285 assert_eq!(cb.open_duration_ms, 30_000);
1286 }
1287
1288 #[test]
1289 fn parse_to_step() {
1290 let yaml = r#"
1291routes:
1292 - id: r6
1293 from: direct:start
1294 steps:
1295 - to: log:out
1296"#;
1297 let parsed: RouteDslRoutes = serde_yml::from_str(yaml).unwrap();
1298 assert_eq!(parsed.routes[0].steps.len(), 1);
1299 }
1300
1301 #[test]
1302 fn parse_delay_short_form() {
1303 let yaml = r#"
1304routes:
1305 - id: r7
1306 from: direct:start
1307 steps:
1308 - delay: 500
1309"#;
1310 let parsed: RouteDslRoutes = serde_yml::from_str(yaml).unwrap();
1311 match &parsed.routes[0].steps[0] {
1312 RouteDslStep::Delay(d) => match &d.delay {
1313 DelayBody::Short(ms) => assert_eq!(*ms, 500),
1314 _ => panic!("expected short form"),
1315 },
1316 _ => panic!("expected delay step"),
1317 }
1318 }
1319
1320 #[test]
1321 fn parse_delay_full_form() {
1322 let yaml = r#"
1323routes:
1324 - id: r8
1325 from: direct:start
1326 steps:
1327 - delay:
1328 delay_ms: 200
1329 dynamic_header: X-Delay
1330"#;
1331 let parsed: RouteDslRoutes = serde_yml::from_str(yaml).unwrap();
1332 match &parsed.routes[0].steps[0] {
1333 RouteDslStep::Delay(d) => match &d.delay {
1334 DelayBody::Full(cfg) => {
1335 assert_eq!(cfg.delay_ms, 200);
1336 assert_eq!(cfg.dynamic_header.as_deref(), Some("X-Delay"));
1337 }
1338 _ => panic!("expected full form"),
1339 },
1340 _ => panic!("expected delay step"),
1341 }
1342 }
1343
1344 #[test]
1345 fn parse_redelivery_policy_defaults() {
1346 let yaml = r#"
1347routes:
1348 - id: r9
1349 from: direct:in
1350 error_handler:
1351 retry:
1352 max_attempts: 3
1353"#;
1354 let parsed: RouteDslRoutes = serde_yml::from_str(yaml).unwrap();
1355 let retry = parsed.routes[0]
1356 .error_handler
1357 .as_ref()
1358 .unwrap()
1359 .retry
1360 .as_ref()
1361 .unwrap();
1362 assert_eq!(retry.max_attempts, 3);
1363 assert_eq!(retry.initial_delay_ms, 100);
1364 assert_eq!(retry.multiplier, 2.0);
1365 assert_eq!(retry.max_delay_ms, 10_000);
1366 assert_eq!(retry.jitter_factor, 0.0);
1367 }
1368
1369 #[test]
1370 fn parse_stream_cache_bool() {
1371 let yaml = r#"
1372routes:
1373 - id: r10
1374 from: direct:start
1375 steps:
1376 - stream_cache: true
1377"#;
1378 let parsed: RouteDslRoutes = serde_yml::from_str(yaml).unwrap();
1379 match &parsed.routes[0].steps[0] {
1380 RouteDslStep::StreamCache(s) => match &s.stream_cache {
1381 StreamCacheBody::Enabled(b) => assert!(*b),
1382 _ => panic!("expected enabled"),
1383 },
1384 _ => panic!("expected stream_cache"),
1385 }
1386 }
1387
1388 #[test]
1389 fn parse_stop_step() {
1390 let yaml = r#"
1391routes:
1392 - id: r11
1393 from: direct:start
1394 steps:
1395 - stop: true
1396"#;
1397 let parsed: RouteDslRoutes = serde_yml::from_str(yaml).unwrap();
1398 match &parsed.routes[0].steps[0] {
1399 RouteDslStep::Stop(s) => assert!(s.stop),
1400 _ => panic!("expected stop"),
1401 }
1402 }
1403
1404 #[test]
1405 fn parse_convert_body_to() {
1406 let yaml = r#"
1407routes:
1408 - id: r12
1409 from: direct:start
1410 steps:
1411 - convert_body_to: json
1412"#;
1413 let parsed: RouteDslRoutes = serde_yml::from_str(yaml).unwrap();
1414 match &parsed.routes[0].steps[0] {
1415 RouteDslStep::ConvertBodyTo(s) => assert_eq!(s.convert_body_to, "json"),
1416 _ => panic!("expected convert_body_to"),
1417 }
1418 }
1419
1420 #[test]
1421 fn parse_marshal_unmarshal() {
1422 let yaml = r#"
1423routes:
1424 - id: r13
1425 from: direct:start
1426 steps:
1427 - marshal: protobuf
1428 - unmarshal: protobuf
1429"#;
1430 let parsed: RouteDslRoutes = serde_yml::from_str(yaml).unwrap();
1431 assert_eq!(parsed.routes[0].steps.len(), 2);
1432 }
1433
1434 #[test]
1435 fn parse_bean_step() {
1436 let yaml = r#"
1437routes:
1438 - id: r14
1439 from: direct:start
1440 steps:
1441 - bean:
1442 name: myBean
1443 method: handle
1444"#;
1445 let parsed: RouteDslRoutes = serde_yml::from_str(yaml).unwrap();
1446 match &parsed.routes[0].steps[0] {
1447 RouteDslStep::Bean(b) => {
1448 assert_eq!(b.bean.name, "myBean");
1449 assert_eq!(b.bean.method, "handle");
1450 }
1451 _ => panic!("expected bean"),
1452 }
1453 }
1454
1455 #[test]
1456 fn parse_script_step() {
1457 let yaml = r#"
1458routes:
1459 - id: r15
1460 from: direct:start
1461 steps:
1462 - script:
1463 language: rhai
1464 source: "1 + 1"
1465"#;
1466 let parsed: RouteDslRoutes = serde_yml::from_str(yaml).unwrap();
1467 match &parsed.routes[0].steps[0] {
1468 RouteDslStep::Script(s) => {
1469 assert_eq!(s.script.language, "rhai");
1470 assert_eq!(s.script.source, "1 + 1");
1471 }
1472 _ => panic!("expected script"),
1473 }
1474 }
1475
1476 #[test]
1479 fn parse_yaml_with_templates_and_templated_routes() {
1480 let yaml = r#"
1481routes:
1482 - id: r1
1483 from: direct:start
1484templates:
1485 - id: http-route
1486 parameters:
1487 - name: path
1488 default_value: /api
1489 description: The REST path
1490 routes:
1491 - id: "instance-route"
1492 from: "rest:{{path}}"
1493 steps:
1494 - to: "log:info"
1495templated_routes:
1496 - route_template_ref: http-route
1497 route_id: my-http-route
1498 parameters:
1499 path: /users
1500"#;
1501 let parsed: RouteDslRoutes = serde_yml::from_str(yaml).unwrap();
1502 assert_eq!(parsed.routes.len(), 1);
1503 assert_eq!(parsed.templates.len(), 1);
1504 assert_eq!(parsed.templated_routes.len(), 1);
1505
1506 let tpl = &parsed.templates[0];
1507 assert_eq!(tpl.id, "http-route");
1508 assert_eq!(tpl.parameters.len(), 1);
1509 assert_eq!(tpl.parameters[0].name, "path");
1510 assert_eq!(tpl.parameters[0].default_value.as_deref(), Some("/api"));
1511 assert_eq!(
1512 tpl.parameters[0].description.as_deref(),
1513 Some("The REST path")
1514 );
1515
1516 let tr = &parsed.templated_routes[0];
1517 assert_eq!(tr.route_template_ref, "http-route");
1518 assert_eq!(tr.route_id.as_deref(), Some("my-http-route"));
1519 assert_eq!(tr.parameters["path"], "/users");
1520 }
1521
1522 #[test]
1523 fn parse_yaml_backward_compat_no_templates() {
1524 let yaml = r#"
1525routes:
1526 - id: r1
1527 from: direct:start
1528 steps:
1529 - to: log:info
1530"#;
1531 let parsed: RouteDslRoutes = serde_yml::from_str(yaml).unwrap();
1532 assert_eq!(parsed.routes.len(), 1);
1533 assert!(parsed.templates.is_empty());
1534 assert!(parsed.templated_routes.is_empty());
1535 }
1536
1537 #[test]
1538 fn parse_yaml_template_with_empty_parameters() {
1539 let yaml = r#"
1540routes: []
1541templates:
1542 - id: simple-tpl
1543 routes:
1544 - id: simple-route
1545 from: timer:tick
1546"#;
1547 let parsed: RouteDslRoutes = serde_yml::from_str(yaml).unwrap();
1548 assert_eq!(parsed.templates.len(), 1);
1549 assert!(parsed.templates[0].parameters.is_empty());
1550 }
1551
1552 #[test]
1553 fn parse_yaml_templated_routes_with_defaults() {
1554 let yaml = r#"
1555routes: []
1556templated_routes:
1557 - route_template_ref: my-tpl
1558"#;
1559 let parsed: RouteDslRoutes = serde_yml::from_str(yaml).unwrap();
1560 assert_eq!(parsed.templated_routes.len(), 1);
1561 let tr = &parsed.templated_routes[0];
1562 assert_eq!(tr.route_template_ref, "my-tpl");
1563 assert!(tr.route_id.is_none());
1564 assert!(tr.parameters.is_empty());
1565 }
1566
1567 #[test]
1570 fn parse_rest_block_basic() {
1571 let yaml = r#"
1572rest:
1573 - host: 0.0.0.0
1574 port: 8080
1575 path: /users
1576 operations:
1577 - method: GET
1578 path: /{id}
1579 operation_id: getUser
1580 to: bean:userService
1581 produces: application/json
1582 - method: POST
1583 operation_id: createUser
1584 consumes: application/json
1585 produces: application/json
1586 success_status: 201
1587 to: bean:createUser
1588"#;
1589 let parsed: RouteDslRoutes = serde_yml::from_str(yaml).unwrap();
1590 assert_eq!(parsed.rest.len(), 1);
1591 let rest = &parsed.rest[0];
1592 assert_eq!(rest.host, "0.0.0.0");
1593 assert_eq!(rest.port, 8080);
1594 assert_eq!(rest.path, "/users");
1595 assert_eq!(rest.operations.len(), 2);
1596
1597 let get_op = &rest.operations[0];
1598 assert_eq!(get_op.method, "GET");
1599 assert_eq!(get_op.path, "/{id}");
1600 assert_eq!(get_op.operation_id.as_deref(), Some("getUser"));
1601 assert_eq!(get_op.to.as_deref(), Some("bean:userService"));
1602 assert_eq!(get_op.produces, "application/json");
1603
1604 let post_op = &rest.operations[1];
1605 assert_eq!(post_op.method, "POST");
1606 assert_eq!(post_op.operation_id.as_deref(), Some("createUser"));
1607 assert_eq!(post_op.consumes, "application/json");
1608 assert_eq!(post_op.produces, "application/json");
1609 assert_eq!(post_op.success_status, Some(201));
1610 assert_eq!(post_op.to.as_deref(), Some("bean:createUser"));
1611 }
1612
1613 #[test]
1614 fn parse_rest_defaults() {
1615 let yaml = r#"
1616rest:
1617 - path: /api
1618 operations:
1619 - method: GET
1620 to: direct:handler
1621"#;
1622 let parsed: RouteDslRoutes = serde_yml::from_str(yaml).unwrap();
1623 let rest = &parsed.rest[0];
1624 assert_eq!(rest.host, "0.0.0.0");
1625 assert_eq!(rest.port, 8080);
1626 assert_eq!(rest.path, "/api");
1627 let op = &rest.operations[0];
1628 assert_eq!(op.method, "GET");
1629 assert_eq!(op.path, "/");
1630 assert_eq!(op.consumes, "application/json");
1631 assert_eq!(op.produces, "application/json");
1632 assert!(op.success_status.is_none());
1633 assert!(op.response.is_none());
1634 assert!(op.steps.is_empty());
1635 }
1636
1637 #[test]
1638 fn parse_rest_empty() {
1639 let yaml = r#"
1640routes:
1641 - id: r1
1642 from: direct:start
1643"#;
1644 let parsed: RouteDslRoutes = serde_yml::from_str(yaml).unwrap();
1645 assert!(parsed.rest.is_empty());
1646 }
1647
1648 #[test]
1649 fn parse_rest_multiple_blocks() {
1650 let yaml = r#"
1651rest:
1652 - path: /users
1653 operations:
1654 - method: GET
1655 to: direct:handleUsers
1656 - path: /orders
1657 operations:
1658 - method: GET
1659 to: direct:handleOrders
1660"#;
1661 let parsed: RouteDslRoutes = serde_yml::from_str(yaml).unwrap();
1662 assert_eq!(parsed.rest.len(), 2);
1663 assert_eq!(parsed.rest[0].path, "/users");
1664 assert_eq!(parsed.rest[1].path, "/orders");
1665 }
1666
1667 #[test]
1668 fn parse_rest_backward_compat_no_rest() {
1669 let yaml = r#"
1670routes:
1671 - id: r1
1672 from: direct:start
1673 steps:
1674 - to: log:info
1675"#;
1676 let parsed: RouteDslRoutes = serde_yml::from_str(yaml).unwrap();
1677 assert_eq!(parsed.routes.len(), 1);
1678 assert!(parsed.rest.is_empty());
1679 }
1680
1681 #[test]
1682 fn parse_rest_and_routes_together() {
1683 let yaml = r#"
1684routes:
1685 - id: r1
1686 from: direct:start
1687 steps:
1688 - to: log:info
1689rest:
1690 - host: localhost
1691 port: 3000
1692 path: /api
1693 operations:
1694 - method: GET
1695 to: direct:apiHandler
1696"#;
1697 let parsed: RouteDslRoutes = serde_yml::from_str(yaml).unwrap();
1698 assert_eq!(parsed.routes.len(), 1);
1699 assert_eq!(parsed.rest.len(), 1);
1700 assert_eq!(parsed.rest[0].host, "localhost");
1701 assert_eq!(parsed.rest[0].port, 3000);
1702 assert_eq!(parsed.rest[0].path, "/api");
1703 assert_eq!(parsed.rest[0].operations.len(), 1);
1704 assert_eq!(parsed.rest[0].operations[0].method, "GET");
1705 }
1706
1707 #[test]
1708 fn parse_rest_with_response() {
1709 let yaml = r#"
1710rest:
1711 - path: /api
1712 operations:
1713 - method: GET
1714 path: /items
1715 to: direct:listItems
1716 response:
1717 description: A list of items
1718 schema:
1719 type: array
1720 items:
1721 type: string
1722 headers:
1723 X-Total-Count:
1724 type: integer
1725"#;
1726 let parsed: RouteDslRoutes = serde_yml::from_str(yaml).unwrap();
1727 let rest = &parsed.rest[0];
1728 let op = &rest.operations[0];
1729 let resp = op.response.as_ref().unwrap();
1730 assert_eq!(resp.description.as_deref(), Some("A list of items"));
1731 assert!(resp.schema.is_some());
1732 assert!(resp.headers.contains_key("X-Total-Count"));
1733 }
1734
1735 #[test]
1736 fn parse_rest_with_request_schema_and_parameters() {
1737 let yaml = r#"
1738rest:
1739 - path: /api
1740 operations:
1741 - method: POST
1742 path: /create
1743 to: direct:create
1744 request_schema:
1745 type: object
1746 properties:
1747 name:
1748 type: string
1749 parameters:
1750 limit:
1751 type: integer
1752 offset:
1753 type: integer
1754"#;
1755 let parsed: RouteDslRoutes = serde_yml::from_str(yaml).unwrap();
1756 let op = &parsed.rest[0].operations[0];
1757 assert!(op.request_schema.is_some());
1758 assert!(op.parameters.contains_key("limit"));
1759 assert!(op.parameters.contains_key("offset"));
1760 }
1761
1762 #[test]
1763 fn circuit_breaker_rejects_typo_field() {
1764 let yaml = r#"
1765failure_threshhold: 5
1766open_duration_ms: 10000
1767"#;
1768 let result: Result<RouteDslCircuitBreaker, _> = serde_yml::from_str(yaml);
1769 assert!(
1770 result.is_err(),
1771 "typo 'failure_threshhold' must be rejected by deny_unknown_fields"
1772 );
1773 }
1774
1775 #[test]
1776 fn parse_rest_multiple_verbs_in_one_block() {
1777 let yaml = r#"
1778rest:
1779 - path: /api
1780 operations:
1781 - method: GET
1782 path: /items
1783 to: direct:getItems
1784 - method: POST
1785 path: /items
1786 to: direct:createItem
1787 - method: PUT
1788 path: /items/{id}
1789 to: direct:updateItem
1790 - method: DELETE
1791 path: /items/{id}
1792 to: direct:deleteItem
1793"#;
1794 let parsed: RouteDslRoutes = serde_yml::from_str(yaml).unwrap();
1795 let ops = &parsed.rest[0].operations;
1796 assert_eq!(ops.len(), 4);
1797 assert_eq!(ops[0].method, "GET");
1798 assert_eq!(ops[1].method, "POST");
1799 assert_eq!(ops[2].method, "PUT");
1800 assert_eq!(ops[3].method, "DELETE");
1801 }
1802
1803 #[cfg(feature = "schema")]
1804 #[test]
1805 fn schema_excludes_set_header_if_absent() {
1806 let schema = schemars::schema_for!(RouteDslStep);
1807 let json = serde_json::to_string(&schema).unwrap();
1808 assert!(
1809 !json.contains("set_header_if_absent"),
1810 "JSON schema must NOT contain set_header_if_absent"
1811 );
1812 }
1813}
1814
1815#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
1816#[derive(Deserialize, Debug, Clone)]
1817#[serde(deny_unknown_fields)]
1818pub struct DynamicRouterStep {
1819 pub dynamic_router: DynamicRouterData,
1820}
1821
1822#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
1823#[derive(Deserialize, Debug, Clone)]
1824#[serde(deny_unknown_fields)]
1825pub struct DynamicRouterData {
1826 #[serde(default)]
1827 pub simple: Option<String>,
1828 #[serde(default)]
1829 pub rhai: Option<String>,
1830 #[serde(default)]
1831 pub language: Option<String>,
1832 #[serde(default)]
1833 pub source: Option<String>,
1834 #[serde(default = "default_uri_delimiter")]
1835 pub uri_delimiter: String,
1836 #[serde(default = "default_cache_size")]
1837 pub cache_size: i32,
1838 #[serde(default)]
1839 pub ignore_invalid_endpoints: bool,
1840 #[serde(default = "default_max_iterations")]
1841 pub max_iterations: usize,
1842}
1843
1844fn default_uri_delimiter() -> String {
1845 ",".to_string()
1846}
1847
1848fn default_cache_size() -> i32 {
1849 1000
1850}
1851
1852fn default_max_iterations() -> usize {
1853 1000
1854}
1855
1856#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
1857#[derive(Deserialize, Debug, Clone)]
1858#[serde(deny_unknown_fields)]
1859pub struct RoutingSlipStep {
1860 pub routing_slip: RoutingSlipData,
1861}
1862
1863#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
1864#[derive(Deserialize, Debug, Clone)]
1865#[serde(deny_unknown_fields)]
1866pub struct RoutingSlipData {
1867 #[serde(default)]
1868 pub simple: Option<String>,
1869 #[serde(default)]
1870 pub rhai: Option<String>,
1871 #[serde(default)]
1872 pub language: Option<String>,
1873 #[serde(default)]
1874 pub source: Option<String>,
1875 #[serde(default = "default_uri_delimiter")]
1876 pub uri_delimiter: String,
1877 #[serde(default = "default_cache_size")]
1878 pub cache_size: i32,
1879 #[serde(default)]
1880 pub ignore_invalid_endpoints: bool,
1881}
1882
1883#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
1884#[derive(Deserialize, Debug, Clone)]
1885#[serde(deny_unknown_fields)]
1886pub struct RecipientListStep {
1887 pub recipient_list: RecipientListData,
1888}
1889
1890#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
1891#[derive(Deserialize, Debug, Clone)]
1892#[serde(deny_unknown_fields)]
1893pub struct RecipientListData {
1894 #[serde(default)]
1895 pub simple: Option<String>,
1896 #[serde(default)]
1897 pub rhai: Option<String>,
1898 #[serde(default)]
1899 pub language: Option<String>,
1900 #[serde(default)]
1901 pub source: Option<String>,
1902 #[serde(default = "default_uri_delimiter")]
1903 pub delimiter: String,
1904 #[serde(default)]
1905 pub parallel: bool,
1906 #[serde(default)]
1907 pub parallel_limit: Option<usize>,
1908 #[serde(default)]
1909 pub stop_on_exception: bool,
1910 #[serde(default)]
1911 pub strategy: Option<String>,
1912}
1913
1914#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
1915#[derive(Deserialize, Debug, Clone)]
1916#[serde(deny_unknown_fields)]
1917pub struct BeanStep {
1918 pub bean: BeanStepData,
1919}
1920
1921#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
1922#[derive(Deserialize, Debug, Clone)]
1923#[serde(deny_unknown_fields)]
1924pub struct BeanStepData {
1925 pub name: String,
1926 pub method: String,
1927}
1928
1929#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
1932#[derive(Deserialize, Debug, Clone)]
1933#[serde(deny_unknown_fields)]
1934pub struct ResequenceStep {
1935 pub resequence: ResequenceData,
1936}
1937
1938#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
1939#[derive(Deserialize, Debug, Clone)]
1940#[serde(deny_unknown_fields)]
1941pub struct ResequenceData {
1942 #[serde(default)]
1943 pub batch: Option<ResequenceBatchYaml>,
1944 #[serde(default)]
1945 pub stream: Option<ResequenceStreamYaml>,
1946}
1947
1948#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
1949#[derive(Deserialize, Debug, Clone)]
1950#[serde(deny_unknown_fields)]
1951pub struct ResequenceBatchYaml {
1952 pub correlation: String,
1953 pub sort: String,
1954 pub completion: ResequenceCompletionYaml,
1955}
1956
1957#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
1958#[derive(Deserialize, Debug, Clone)]
1959#[serde(deny_unknown_fields)]
1960pub struct ResequenceCompletionYaml {
1961 #[serde(default)]
1962 pub size: Option<usize>,
1963 #[serde(default)]
1964 pub timeout: Option<u64>,
1965 #[serde(default)]
1966 pub size_or_timeout: Option<Vec<u64>>,
1967}
1968
1969#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
1973#[derive(Deserialize, Debug, Clone)]
1974#[serde(deny_unknown_fields)]
1975pub struct ResequenceStreamYaml {
1976 pub sequence: String,
1977 #[serde(default = "default_stream_capacity")]
1978 pub capacity: usize,
1979 #[serde(default = "default_gap_timeout")]
1980 pub gap_timeout: u64,
1981 #[serde(default)]
1982 pub on_gap: StreamGapPolicyYaml,
1983 #[serde(default)]
1984 pub on_capacity_exceeded: StreamCapacityPolicyYaml,
1985 #[serde(default)]
1986 pub dedup: bool,
1987}
1988
1989fn default_stream_capacity() -> usize {
1990 1000
1991}
1992
1993fn default_gap_timeout() -> u64 {
1994 5000
1995}
1996
1997#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
1999#[derive(Deserialize, Debug, Clone, Default, PartialEq, Eq)]
2000#[serde(rename_all = "snake_case")]
2001pub enum StreamGapPolicyYaml {
2002 #[default]
2003 EmitPartial,
2004 DropAndLog,
2005}
2006
2007#[cfg_attr(feature = "schema", derive(schemars::JsonSchema, ts_rs::TS))]
2009#[derive(Deserialize, Debug, Clone, Default, PartialEq, Eq)]
2010#[serde(rename_all = "snake_case")]
2011pub enum StreamCapacityPolicyYaml {
2012 #[default]
2013 LogAndDrop,
2014 DropOldest,
2015}