1use std::sync::Arc;
5
6use camel_api::UnitOfWorkConfig;
7use camel_api::circuit_breaker::CircuitBreakerConfig;
8use camel_api::error_handler::ErrorHandlerConfig;
9use camel_api::loop_eip::LoopConfig;
10use camel_api::security_policy::SecurityPolicyConfig;
11use camel_api::{
12 AggregatorConfig, BoxProcessor, FilterPredicate, MulticastConfig, ResequencePolicyConfig,
13 SplitterConfig,
14};
15use camel_auth::TokenAuthenticator;
16use camel_component_api::ConcurrencyModel;
17
18pub struct WhenStep {
20 pub predicate: FilterPredicate,
21 pub steps: Vec<BuilderStep>,
22}
23
24pub use camel_api::declarative::{LanguageExpressionDef, ValueSourceDef};
25
26#[derive(Debug)]
28pub struct DeclarativeWhenStep {
29 pub predicate: LanguageExpressionDef,
30 pub steps: Vec<BuilderStep>,
31}
32
33#[derive(Debug)]
35pub struct DoTryCatchClauseBuilder {
36 pub exception: Option<Vec<String>>,
37 pub when: Option<LanguageExpressionDef>,
38 pub on_when: Option<LanguageExpressionDef>,
39 pub disposition: camel_api::error_handler::ExceptionDisposition,
40 pub steps: Vec<BuilderStep>,
41}
42
43#[derive(Debug)]
45pub struct DoTryFinallyBuilder {
46 pub on_when: Option<LanguageExpressionDef>,
47 pub steps: Vec<BuilderStep>,
48}
49
50pub enum BuilderStep {
52 Processor(BoxProcessor),
54 To(String),
56 Stop,
58 Log {
60 level: camel_processor::LogLevel,
61 message: String,
62 },
63 DeclarativeSetHeader {
65 key: String,
66 value: ValueSourceDef,
67 },
68 DeclarativeSetProperty {
69 key: String,
70 value_source: ValueSourceDef,
71 },
72 DeclarativeSetBody {
74 value: ValueSourceDef,
75 },
76 DeclarativeFilter {
78 predicate: LanguageExpressionDef,
79 steps: Vec<BuilderStep>,
80 },
81 DeclarativeChoice {
83 whens: Vec<DeclarativeWhenStep>,
84 otherwise: Option<Vec<BuilderStep>>,
85 },
86 DeclarativeScript {
88 expression: LanguageExpressionDef,
89 },
90 DeclarativeFunction {
91 definition: camel_api::FunctionDefinition,
92 },
93 DeclarativeSplit {
95 expression: LanguageExpressionDef,
96 aggregation: camel_api::splitter::AggregationStrategy,
97 parallel: bool,
98 parallel_limit: Option<usize>,
99 stop_on_exception: bool,
100 steps: Vec<BuilderStep>,
101 },
102 DeclarativeStreamSplit {
104 stream_config: camel_api::StreamSplitConfig,
105 aggregation: camel_api::splitter::AggregationStrategy,
106 stop_on_exception: bool,
107 steps: Vec<BuilderStep>,
108 },
109 DeclarativeDynamicRouter {
110 expression: LanguageExpressionDef,
111 uri_delimiter: String,
112 cache_size: i32,
113 ignore_invalid_endpoints: bool,
114 max_iterations: usize,
115 },
116 DeclarativeRoutingSlip {
117 expression: LanguageExpressionDef,
118 uri_delimiter: String,
119 cache_size: i32,
120 ignore_invalid_endpoints: bool,
121 },
122 Split {
124 config: SplitterConfig,
125 steps: Vec<BuilderStep>,
126 },
127 Aggregate {
129 config: AggregatorConfig,
130 },
131 Filter {
133 predicate: FilterPredicate,
134 steps: Vec<BuilderStep>,
135 },
136 Choice {
139 whens: Vec<WhenStep>,
140 otherwise: Option<Vec<BuilderStep>>,
141 },
142 WireTap {
144 uri: String,
145 },
146 Multicast {
148 steps: Vec<BuilderStep>,
149 config: MulticastConfig,
150 },
151 DeclarativeLog {
153 level: camel_processor::LogLevel,
154 message: ValueSourceDef,
155 },
156 Bean {
158 name: String,
159 method: String,
160 },
161 Script {
164 language: String,
165 script: String,
166 },
167 Throttle {
169 config: camel_api::ThrottlerConfig,
170 steps: Vec<BuilderStep>,
171 },
172 LoadBalance {
174 config: camel_api::LoadBalancerConfig,
175 steps: Vec<BuilderStep>,
176 },
177 DynamicRouter {
179 config: camel_api::DynamicRouterConfig,
180 },
181 RoutingSlip {
182 config: camel_api::RoutingSlipConfig,
183 },
184 RecipientList {
185 config: camel_api::recipient_list::RecipientListConfig,
186 },
187 DeclarativeRecipientList {
188 expression: LanguageExpressionDef,
189 delimiter: String,
190 parallel: bool,
191 parallel_limit: Option<usize>,
192 stop_on_exception: bool,
193 aggregation: String,
194 },
195 Delay {
196 config: camel_api::DelayConfig,
197 },
198 Loop {
200 config: LoopConfig,
201 steps: Vec<BuilderStep>,
202 },
203 DeclarativeLoop {
205 count: Option<usize>,
206 while_predicate: Option<LanguageExpressionDef>,
207 steps: Vec<BuilderStep>,
208 },
209 Enrich {
211 uri: String,
212 strategy: Option<String>,
213 timeout_ms: Option<u64>,
214 },
215 PollEnrich {
217 uri: String,
218 strategy: Option<String>,
219 timeout_ms: Option<u64>,
220 },
221 Validate {
224 predicate: LanguageExpressionDef,
225 },
226 ClaimCheck {
229 repository: String,
230 operation: String,
231 key: LanguageExpressionDef,
232 },
233 Sampling {
237 period: usize,
238 },
239 Sort {
242 expression: LanguageExpressionDef,
243 reverse: bool,
244 },
245 IdempotentConsumer {
249 repository: String,
250 expression: LanguageExpressionDef,
251 steps: Vec<BuilderStep>,
252 eager: bool,
253 remove_on_failure: bool,
254 },
255 DeclarativeDoTry {
257 try_steps: Vec<BuilderStep>,
258 catch: Vec<DoTryCatchClauseBuilder>,
259 finally: Option<DoTryFinallyBuilder>,
260 },
261 Resequence {
264 policy_config: ResequencePolicyConfig,
265 },
266}
267
268impl std::fmt::Debug for BuilderStep {
269 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
270 match self {
271 BuilderStep::Processor(_) => write!(f, "BuilderStep::Processor(...)"),
272 BuilderStep::To(uri) => write!(f, "BuilderStep::To({uri:?})"),
273 BuilderStep::Stop => write!(f, "BuilderStep::Stop"),
274 BuilderStep::Log { level, message } => write!(
275 f,
276 "BuilderStep::Log {{ level: {level:?}, message: {message:?} }}"
277 ),
278 BuilderStep::DeclarativeSetHeader { key, .. } => {
279 write!(
280 f,
281 "BuilderStep::DeclarativeSetHeader {{ key: {key:?}, .. }}"
282 )
283 }
284 BuilderStep::DeclarativeSetBody { .. } => {
285 write!(f, "BuilderStep::DeclarativeSetBody {{ .. }}")
286 }
287 BuilderStep::DeclarativeSetProperty { key, .. } => {
288 write!(
289 f,
290 "BuilderStep::DeclarativeSetProperty {{ key: {key:?}, .. }}"
291 )
292 }
293 BuilderStep::DeclarativeFilter { steps, .. } => {
294 write!(
295 f,
296 "BuilderStep::DeclarativeFilter {{ steps: {steps:?}, .. }}"
297 )
298 }
299 BuilderStep::DeclarativeChoice { whens, otherwise } => {
300 write!(
301 f,
302 "BuilderStep::DeclarativeChoice {{ whens: {} clause(s), otherwise: {} }}",
303 whens.len(),
304 if otherwise.is_some() { "Some" } else { "None" }
305 )
306 }
307 BuilderStep::DeclarativeScript { expression } => write!(
308 f,
309 "BuilderStep::DeclarativeScript {{ language: {:?}, .. }}",
310 expression.language
311 ),
312 BuilderStep::DeclarativeFunction { definition } => write!(
313 f,
314 "BuilderStep::DeclarativeFunction {{ id: {:?}, runtime: {:?}, .. }}",
315 definition.id, definition.runtime
316 ),
317 BuilderStep::DeclarativeSplit { steps, .. } => {
318 write!(
319 f,
320 "BuilderStep::DeclarativeSplit {{ steps: {steps:?}, .. }}"
321 )
322 }
323 BuilderStep::DeclarativeStreamSplit {
324 steps,
325 stream_config,
326 ..
327 } => {
328 write!(
329 f,
330 "BuilderStep::DeclarativeStreamSplit {{ format: {:?}, steps: {} step(s) }}",
331 stream_config.format,
332 steps.len()
333 )
334 }
335 BuilderStep::DeclarativeDynamicRouter { expression, .. } => write!(
336 f,
337 "BuilderStep::DeclarativeDynamicRouter {{ language: {:?}, .. }}",
338 expression.language
339 ),
340 BuilderStep::DeclarativeRoutingSlip { expression, .. } => write!(
341 f,
342 "BuilderStep::DeclarativeRoutingSlip {{ language: {:?}, .. }}",
343 expression.language
344 ),
345 BuilderStep::Split { steps, .. } => {
346 write!(f, "BuilderStep::Split {{ steps: {steps:?}, .. }}")
347 }
348 BuilderStep::Aggregate { .. } => write!(f, "BuilderStep::Aggregate {{ .. }}"),
349 BuilderStep::Filter { steps, .. } => {
350 write!(f, "BuilderStep::Filter {{ steps: {steps:?}, .. }}")
351 }
352 BuilderStep::Choice { whens, otherwise } => {
353 write!(
354 f,
355 "BuilderStep::Choice {{ whens: {} clause(s), otherwise: {} }}",
356 whens.len(),
357 if otherwise.is_some() { "Some" } else { "None" }
358 )
359 }
360 BuilderStep::WireTap { uri } => write!(f, "BuilderStep::WireTap {{ uri: {uri:?} }}"),
361 BuilderStep::Multicast { steps, .. } => {
362 write!(f, "BuilderStep::Multicast {{ steps: {steps:?}, .. }}")
363 }
364 BuilderStep::DeclarativeLog { level, .. } => {
365 write!(f, "BuilderStep::DeclarativeLog {{ level: {level:?}, .. }}")
366 }
367 BuilderStep::Bean { name, method } => {
368 write!(
369 f,
370 "BuilderStep::Bean {{ name: {name:?}, method: {method:?} }}"
371 )
372 }
373 BuilderStep::Script { language, .. } => {
374 write!(f, "BuilderStep::Script {{ language: {language:?}, .. }}")
375 }
376 BuilderStep::Throttle { steps, .. } => {
377 write!(f, "BuilderStep::Throttle {{ steps: {steps:?}, .. }}")
378 }
379 BuilderStep::LoadBalance { steps, .. } => {
380 write!(f, "BuilderStep::LoadBalance {{ steps: {steps:?}, .. }}")
381 }
382 BuilderStep::DynamicRouter { .. } => {
383 write!(f, "BuilderStep::DynamicRouter {{ .. }}")
384 }
385 BuilderStep::RoutingSlip { .. } => {
386 write!(f, "BuilderStep::RoutingSlip {{ .. }}")
387 }
388 BuilderStep::RecipientList { .. } => {
389 write!(f, "BuilderStep::RecipientList {{ .. }}")
390 }
391 BuilderStep::DeclarativeRecipientList {
392 expression,
393 aggregation,
394 ..
395 } => write!(
396 f,
397 "BuilderStep::DeclarativeRecipientList {{ language: {:?}, aggregation: {:?}, .. }}",
398 expression.language, aggregation
399 ),
400 BuilderStep::Delay { config } => {
401 write!(f, "BuilderStep::Delay {{ config: {:?} }}", config)
402 }
403 BuilderStep::Loop { config, steps } => {
404 write!(
405 f,
406 "BuilderStep::Loop {{ config: {:?}, steps: {} }}",
407 config.mode_name(),
408 steps.len()
409 )
410 }
411 BuilderStep::DeclarativeLoop {
412 count,
413 while_predicate,
414 steps,
415 } => {
416 write!(
417 f,
418 "BuilderStep::DeclarativeLoop {{ count: {:?}, while: {}, steps: {} }}",
419 count,
420 while_predicate.is_some(),
421 steps.len()
422 )
423 }
424 BuilderStep::Validate { predicate } => {
425 write!(
426 f,
427 "BuilderStep::Validate {{ language: {:?}, source: {:?} }}",
428 predicate.language, predicate.source
429 )
430 }
431 BuilderStep::IdempotentConsumer {
432 repository,
433 expression,
434 steps,
435 eager,
436 remove_on_failure,
437 } => {
438 write!(
439 f,
440 "BuilderStep::IdempotentConsumer {{ repository: {repository:?}, language: {:?}, steps: {}, eager: {eager}, remove_on_failure: {remove_on_failure} }}",
441 expression.language,
442 steps.len()
443 )
444 }
445 BuilderStep::Enrich {
446 uri,
447 strategy,
448 timeout_ms,
449 } => {
450 write!(
451 f,
452 "BuilderStep::Enrich {{ uri: {uri:?}, strategy: {strategy:?}, timeout_ms: {timeout_ms:?} }}"
453 )
454 }
455 BuilderStep::PollEnrich {
456 uri,
457 strategy,
458 timeout_ms,
459 } => {
460 write!(
461 f,
462 "BuilderStep::PollEnrich {{ uri: {uri:?}, strategy: {strategy:?}, timeout_ms: {timeout_ms:?} }}"
463 )
464 }
465 BuilderStep::ClaimCheck {
466 repository,
467 operation,
468 key,
469 } => {
470 write!(
471 f,
472 "BuilderStep::ClaimCheck {{ repository: {repository:?}, operation: {operation:?}, \
473 key: {{ language: {:?}, source: {:?} }} }}",
474 key.language, key.source
475 )
476 }
477 BuilderStep::Sampling { period } => {
478 write!(f, "BuilderStep::Sampling {{ period: {period} }}")
479 }
480 BuilderStep::Sort {
481 expression,
482 reverse,
483 } => {
484 write!(
485 f,
486 "BuilderStep::Sort {{ language: {:?}, source: {:?}, reverse: {reverse} }}",
487 expression.language, expression.source
488 )
489 }
490 BuilderStep::DeclarativeDoTry {
491 try_steps,
492 catch,
493 finally,
494 } => {
495 write!(
496 f,
497 "BuilderStep::DeclarativeDoTry {{ try_steps: {} step(s), catch: {} clause(s), finally: {} }}",
498 try_steps.len(),
499 catch.len(),
500 if finally.is_some() { "Some" } else { "None" }
501 )
502 }
503 BuilderStep::Resequence { .. } => write!(f, "BuilderStep::Resequence {{ .. }}"),
504 }
505 }
506}
507
508pub struct RouteDefinition {
510 pub(crate) from_uri: String,
511 pub(crate) steps: Vec<BuilderStep>,
512 pub(crate) error_handler: Option<ErrorHandlerConfig>,
514 pub(crate) circuit_breaker: Option<CircuitBreakerConfig>,
516 pub(crate) security_policy: Option<SecurityPolicyConfig>,
517 pub(crate) security_authenticator: Option<Arc<dyn TokenAuthenticator>>,
519 pub(crate) unit_of_work: Option<UnitOfWorkConfig>,
521 pub(crate) concurrency: Option<ConcurrencyModel>,
524 pub(crate) route_id: String,
526 pub(crate) auto_startup: bool,
528 pub(crate) startup_order: i32,
530 pub(crate) source_hash: Option<u64>,
531}
532
533impl RouteDefinition {
534 pub fn new(from_uri: impl Into<String>, steps: Vec<BuilderStep>) -> Self {
536 Self {
537 from_uri: from_uri.into(),
538 steps,
539 error_handler: None,
540 circuit_breaker: None,
541 security_policy: None,
542 security_authenticator: None,
543 unit_of_work: None,
544 concurrency: None,
545 route_id: String::new(), auto_startup: true,
547 startup_order: 1000,
548 source_hash: None,
549 }
550 }
551
552 pub fn from_uri(&self) -> &str {
554 &self.from_uri
555 }
556
557 pub fn steps(&self) -> &[BuilderStep] {
559 &self.steps
560 }
561
562 pub fn with_error_handler(mut self, config: ErrorHandlerConfig) -> Self {
564 self.error_handler = Some(config);
565 self
566 }
567
568 pub fn error_handler_config(&self) -> Option<&ErrorHandlerConfig> {
570 self.error_handler.as_ref()
571 }
572
573 pub fn with_circuit_breaker(mut self, config: CircuitBreakerConfig) -> Self {
575 self.circuit_breaker = Some(config);
576 self
577 }
578
579 pub fn with_security_policy(mut self, config: SecurityPolicyConfig) -> Self {
581 self.security_policy = Some(config);
582 self
583 }
584
585 pub fn with_security_authenticator(
587 mut self,
588 authenticator: Arc<dyn TokenAuthenticator>,
589 ) -> Self {
590 self.security_authenticator = Some(authenticator);
591 self
592 }
593
594 pub fn with_unit_of_work(mut self, config: UnitOfWorkConfig) -> Self {
596 self.unit_of_work = Some(config);
597 self
598 }
599
600 pub fn unit_of_work_config(&self) -> Option<&UnitOfWorkConfig> {
602 self.unit_of_work.as_ref()
603 }
604
605 pub fn circuit_breaker_config(&self) -> Option<&CircuitBreakerConfig> {
607 self.circuit_breaker.as_ref()
608 }
609
610 pub fn security_policy_config(&self) -> Option<&SecurityPolicyConfig> {
611 self.security_policy.as_ref()
612 }
613
614 pub fn security_authenticator(&self) -> Option<&Arc<dyn TokenAuthenticator>> {
615 self.security_authenticator.as_ref()
616 }
617
618 pub fn concurrency_override(&self) -> Option<&ConcurrencyModel> {
620 self.concurrency.as_ref()
621 }
622
623 pub fn with_concurrency(mut self, model: ConcurrencyModel) -> Self {
625 self.concurrency = Some(model);
626 self
627 }
628
629 pub fn route_id(&self) -> &str {
631 &self.route_id
632 }
633
634 pub fn auto_startup(&self) -> bool {
636 self.auto_startup
637 }
638
639 pub fn startup_order(&self) -> i32 {
641 self.startup_order
642 }
643
644 pub fn with_route_id(mut self, id: impl Into<String>) -> Self {
646 self.route_id = id.into();
647 self
648 }
649
650 pub fn with_auto_startup(mut self, auto: bool) -> Self {
652 self.auto_startup = auto;
653 self
654 }
655
656 pub fn with_startup_order(mut self, order: i32) -> Self {
658 self.startup_order = order;
659 self
660 }
661
662 pub fn with_source_hash(mut self, hash: u64) -> Self {
663 self.source_hash = Some(hash);
664 self
665 }
666
667 pub fn source_hash(&self) -> Option<u64> {
668 self.source_hash
669 }
670
671 pub fn to_info(&self) -> RouteDefinitionInfo {
674 RouteDefinitionInfo {
675 route_id: self.route_id.clone(),
676 auto_startup: self.auto_startup,
677 startup_order: self.startup_order,
678 source_hash: self.source_hash,
679 }
680 }
681}
682
683#[derive(Clone)]
689pub struct RouteDefinitionInfo {
690 route_id: String,
691 auto_startup: bool,
692 startup_order: i32,
693 pub(crate) source_hash: Option<u64>,
694}
695
696impl RouteDefinitionInfo {
697 pub fn route_id(&self) -> &str {
699 &self.route_id
700 }
701
702 pub fn auto_startup(&self) -> bool {
704 self.auto_startup
705 }
706
707 pub fn startup_order(&self) -> i32 {
709 self.startup_order
710 }
711
712 pub fn source_hash(&self) -> Option<u64> {
713 self.source_hash
714 }
715}
716
717#[cfg(test)]
718mod tests {
719 use super::*;
720
721 #[test]
722 fn test_builder_step_multicast_variant() {
723 use camel_api::MulticastConfig;
724
725 let step = BuilderStep::Multicast {
726 steps: vec![BuilderStep::To("direct:a".into())],
727 config: MulticastConfig::new(),
728 };
729
730 assert!(matches!(step, BuilderStep::Multicast { .. }));
731 }
732
733 #[test]
734 fn test_route_definition_defaults() {
735 let def = RouteDefinition::new("direct:test", vec![]).with_route_id("test-route");
736 assert_eq!(def.route_id(), "test-route");
737 assert!(def.auto_startup());
738 assert_eq!(def.startup_order(), 1000);
739 }
740
741 #[test]
742 fn test_route_definition_builders() {
743 let def = RouteDefinition::new("direct:test", vec![])
744 .with_route_id("my-route")
745 .with_auto_startup(false)
746 .with_startup_order(50);
747 assert_eq!(def.route_id(), "my-route");
748 assert!(!def.auto_startup());
749 assert_eq!(def.startup_order(), 50);
750 }
751
752 #[test]
753 fn test_route_definition_accessors_cover_core_fields() {
754 let def = RouteDefinition::new("direct:in", vec![BuilderStep::To("mock:out".into())])
755 .with_route_id("accessor-route");
756
757 assert_eq!(def.from_uri(), "direct:in");
758 assert_eq!(def.steps().len(), 1);
759 assert!(matches!(def.steps()[0], BuilderStep::To(_)));
760 }
761
762 #[test]
763 fn test_route_definition_error_handler_circuit_breaker_and_concurrency_accessors() {
764 use camel_api::circuit_breaker::CircuitBreakerConfig;
765 use camel_api::error_handler::ErrorHandlerConfig;
766 use camel_component_api::ConcurrencyModel;
767
768 let def = RouteDefinition::new("direct:test", vec![])
769 .with_route_id("eh-route")
770 .with_error_handler(ErrorHandlerConfig::dead_letter_channel("log:dlc"))
771 .with_circuit_breaker(CircuitBreakerConfig::new())
772 .with_concurrency(ConcurrencyModel::Concurrent { max: Some(4) });
773
774 let eh = def
775 .error_handler_config()
776 .expect("error handler should be set");
777 assert_eq!(eh.dlc_uri.as_deref(), Some("log:dlc"));
778 assert!(def.circuit_breaker_config().is_some());
779 assert!(matches!(
780 def.concurrency_override(),
781 Some(ConcurrencyModel::Concurrent { max: Some(4) })
782 ));
783 }
784
785 #[test]
786 fn test_builder_step_debug_covers_many_variants() {
787 use camel_api::splitter::{AggregationStrategy, SplitterConfig, split_body_lines};
788 use camel_api::{
789 DynamicRouterConfig, Exchange, IdentityProcessor, RoutingSlipConfig, Value,
790 };
791 use std::sync::Arc;
792
793 let expr = LanguageExpressionDef {
794 language: "simple".into(),
795 source: "${body}".into(),
796 };
797
798 let steps = vec![
799 BuilderStep::Processor(BoxProcessor::new(IdentityProcessor)),
800 BuilderStep::To("mock:out".into()),
801 BuilderStep::Stop,
802 BuilderStep::Log {
803 level: camel_processor::LogLevel::Info,
804 message: "hello".into(),
805 },
806 BuilderStep::DeclarativeSetHeader {
807 key: "k".into(),
808 value: ValueSourceDef::Literal(Value::String("v".into())),
809 },
810 BuilderStep::DeclarativeSetBody {
811 value: ValueSourceDef::Expression(expr.clone()),
812 },
813 BuilderStep::DeclarativeFilter {
814 predicate: expr.clone(),
815 steps: vec![BuilderStep::Stop],
816 },
817 BuilderStep::DeclarativeChoice {
818 whens: vec![DeclarativeWhenStep {
819 predicate: expr.clone(),
820 steps: vec![BuilderStep::Stop],
821 }],
822 otherwise: Some(vec![BuilderStep::Stop]),
823 },
824 BuilderStep::DeclarativeScript {
825 expression: expr.clone(),
826 },
827 BuilderStep::DeclarativeSplit {
828 expression: expr.clone(),
829 aggregation: AggregationStrategy::Original,
830 parallel: false,
831 parallel_limit: Some(2),
832 stop_on_exception: true,
833 steps: vec![BuilderStep::Stop],
834 },
835 BuilderStep::Split {
836 config: SplitterConfig::new(split_body_lines()),
837 steps: vec![BuilderStep::Stop],
838 },
839 BuilderStep::Aggregate {
840 config: camel_api::AggregatorConfig::correlate_by("id")
841 .complete_when_size(1)
842 .build()
843 .unwrap(),
844 },
845 BuilderStep::Filter {
846 predicate: Arc::new(|_: &Exchange| true),
847 steps: vec![BuilderStep::Stop],
848 },
849 BuilderStep::WireTap {
850 uri: "mock:tap".into(),
851 },
852 BuilderStep::DeclarativeLog {
853 level: camel_processor::LogLevel::Info,
854 message: ValueSourceDef::Expression(expr.clone()),
855 },
856 BuilderStep::Bean {
857 name: "bean".into(),
858 method: "call".into(),
859 },
860 BuilderStep::Script {
861 language: "rhai".into(),
862 script: "body".into(),
863 },
864 BuilderStep::Throttle {
865 config: camel_api::ThrottlerConfig::new(10, std::time::Duration::from_millis(10)),
866 steps: vec![BuilderStep::Stop],
867 },
868 BuilderStep::LoadBalance {
869 config: camel_api::LoadBalancerConfig::round_robin(),
870 steps: vec![BuilderStep::To("mock:l1".into())],
871 },
872 BuilderStep::DynamicRouter {
873 config: DynamicRouterConfig::new(Arc::new(|_| Some("mock:dr".into()))),
874 },
875 BuilderStep::RoutingSlip {
876 config: RoutingSlipConfig::new(Arc::new(|_| Some("mock:rs".into()))),
877 },
878 ];
879
880 for step in steps {
881 let dbg = format!("{step:?}");
882 assert!(!dbg.is_empty());
883 }
884 }
885
886 #[test]
887 fn test_route_definition_to_info_preserves_metadata() {
888 let info = RouteDefinition::new("direct:test", vec![])
889 .with_route_id("meta-route")
890 .with_auto_startup(false)
891 .with_startup_order(7)
892 .to_info();
893
894 assert_eq!(info.route_id(), "meta-route");
895 assert!(!info.auto_startup());
896 assert_eq!(info.startup_order(), 7);
897 }
898
899 #[test]
900 fn test_choice_builder_step_debug() {
901 use camel_api::{Exchange, FilterPredicate};
902 use std::sync::Arc;
903
904 fn always_true(_: &Exchange) -> bool {
905 true
906 }
907
908 let step = BuilderStep::Choice {
909 whens: vec![WhenStep {
910 predicate: Arc::new(always_true) as FilterPredicate,
911 steps: vec![BuilderStep::To("mock:a".into())],
912 }],
913 otherwise: None,
914 };
915 let debug = format!("{step:?}");
916 assert!(debug.contains("Choice"));
917 }
918
919 #[test]
920 fn test_route_definition_unit_of_work() {
921 use camel_api::UnitOfWorkConfig;
922 let config = UnitOfWorkConfig {
923 on_complete: Some("log:complete".into()),
924 on_failure: Some("log:failed".into()),
925 };
926 let def = RouteDefinition::new("direct:test", vec![])
927 .with_route_id("uow-test")
928 .with_unit_of_work(config.clone());
929 assert_eq!(
930 def.unit_of_work_config().unwrap().on_complete.as_deref(),
931 Some("log:complete")
932 );
933 assert_eq!(
934 def.unit_of_work_config().unwrap().on_failure.as_deref(),
935 Some("log:failed")
936 );
937
938 let def_no_uow = RouteDefinition::new("direct:test", vec![]).with_route_id("no-uow");
939 assert!(def_no_uow.unit_of_work_config().is_none());
940 }
941
942 #[test]
943 fn test_route_definition_security_policy_accessor() {
944 use async_trait::async_trait;
945 use camel_api::CamelError;
946 use camel_api::Exchange;
947 use camel_api::security_policy::{
948 AuthorizationDecision, Principal, SecurityPolicy, SecurityPolicyConfig,
949 };
950
951 struct StubPolicy;
952 #[async_trait]
953 impl SecurityPolicy for StubPolicy {
954 async fn evaluate(
955 &self,
956 _exchange: &mut Exchange,
957 ) -> Result<AuthorizationDecision, CamelError> {
958 Ok(AuthorizationDecision::Granted {
959 principal: Principal {
960 subject: "test".into(),
961 issuer: "test".into(),
962 audience: vec![],
963 scopes: vec![],
964 roles: vec![],
965 claims: serde_json::Value::Null,
966 },
967 })
968 }
969 }
970
971 let def_no_sp = RouteDefinition::new("direct:test", vec![]).with_route_id("no-sp");
972 assert!(def_no_sp.security_policy_config().is_none());
973
974 let def = RouteDefinition::new("direct:test", vec![])
975 .with_route_id("sp-test")
976 .with_security_policy(SecurityPolicyConfig::new(StubPolicy));
977 assert!(def.security_policy_config().is_some());
978 }
979
980 #[test]
981 fn test_route_definition_security_authenticator_accessor() {
982 use camel_api::security_policy::Principal;
983
984 struct TestAuth;
985 #[async_trait::async_trait]
986 impl TokenAuthenticator for TestAuth {
987 async fn authenticate_bearer(
988 &self,
989 _token: &str,
990 ) -> Result<Principal, camel_api::CamelError> {
991 Ok(Principal {
992 subject: "test".into(),
993 issuer: "test".into(),
994 audience: vec![],
995 scopes: vec![],
996 roles: vec![],
997 claims: serde_json::Value::Null,
998 })
999 }
1000 }
1001
1002 let def_no_auth = RouteDefinition::new("direct:test".to_string(), vec![]);
1003 assert!(def_no_auth.security_authenticator().is_none());
1004
1005 let auth = Arc::new(TestAuth);
1006 let def = RouteDefinition::new("direct:test".to_string(), vec![])
1007 .with_security_authenticator(auth);
1008 assert!(def.security_authenticator().is_some());
1009 }
1010}