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