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 {
230 repository: String,
231 operation: String,
232 key: LanguageExpressionDef,
233 filter: Option<String>,
234 },
235 Sampling {
239 period: usize,
240 },
241 Sort {
244 expression: LanguageExpressionDef,
245 reverse: bool,
246 },
247 IdempotentConsumer {
251 repository: String,
252 expression: LanguageExpressionDef,
253 steps: Vec<BuilderStep>,
254 eager: bool,
255 remove_on_failure: bool,
256 },
257 DeclarativeDoTry {
259 try_steps: Vec<BuilderStep>,
260 catch: Vec<DoTryCatchClauseBuilder>,
261 finally: Option<DoTryFinallyBuilder>,
262 },
263 Resequence {
266 policy_config: ResequencePolicyConfig,
267 },
268}
269
270impl std::fmt::Debug for BuilderStep {
271 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
272 match self {
273 BuilderStep::Processor(_) => write!(f, "BuilderStep::Processor(...)"),
274 BuilderStep::To(uri) => write!(f, "BuilderStep::To({uri:?})"),
275 BuilderStep::Stop => write!(f, "BuilderStep::Stop"),
276 BuilderStep::Log { level, message } => write!(
277 f,
278 "BuilderStep::Log {{ level: {level:?}, message: {message:?} }}"
279 ),
280 BuilderStep::DeclarativeSetHeader { key, .. } => {
281 write!(
282 f,
283 "BuilderStep::DeclarativeSetHeader {{ key: {key:?}, .. }}"
284 )
285 }
286 BuilderStep::DeclarativeSetBody { .. } => {
287 write!(f, "BuilderStep::DeclarativeSetBody {{ .. }}")
288 }
289 BuilderStep::DeclarativeSetProperty { key, .. } => {
290 write!(
291 f,
292 "BuilderStep::DeclarativeSetProperty {{ key: {key:?}, .. }}"
293 )
294 }
295 BuilderStep::DeclarativeFilter { steps, .. } => {
296 write!(
297 f,
298 "BuilderStep::DeclarativeFilter {{ steps: {steps:?}, .. }}"
299 )
300 }
301 BuilderStep::DeclarativeChoice { whens, otherwise } => {
302 write!(
303 f,
304 "BuilderStep::DeclarativeChoice {{ whens: {} clause(s), otherwise: {} }}",
305 whens.len(),
306 if otherwise.is_some() { "Some" } else { "None" }
307 )
308 }
309 BuilderStep::DeclarativeScript { expression } => write!(
310 f,
311 "BuilderStep::DeclarativeScript {{ language: {:?}, .. }}",
312 expression.language
313 ),
314 BuilderStep::DeclarativeFunction { definition } => write!(
315 f,
316 "BuilderStep::DeclarativeFunction {{ id: {:?}, runtime: {:?}, .. }}",
317 definition.id, definition.runtime
318 ),
319 BuilderStep::DeclarativeSplit { steps, .. } => {
320 write!(
321 f,
322 "BuilderStep::DeclarativeSplit {{ steps: {steps:?}, .. }}"
323 )
324 }
325 BuilderStep::DeclarativeStreamSplit {
326 steps,
327 stream_config,
328 ..
329 } => {
330 write!(
331 f,
332 "BuilderStep::DeclarativeStreamSplit {{ format: {:?}, steps: {} step(s) }}",
333 stream_config.format,
334 steps.len()
335 )
336 }
337 BuilderStep::DeclarativeDynamicRouter { expression, .. } => write!(
338 f,
339 "BuilderStep::DeclarativeDynamicRouter {{ language: {:?}, .. }}",
340 expression.language
341 ),
342 BuilderStep::DeclarativeRoutingSlip { expression, .. } => write!(
343 f,
344 "BuilderStep::DeclarativeRoutingSlip {{ language: {:?}, .. }}",
345 expression.language
346 ),
347 BuilderStep::Split { steps, .. } => {
348 write!(f, "BuilderStep::Split {{ steps: {steps:?}, .. }}")
349 }
350 BuilderStep::Aggregate { .. } => write!(f, "BuilderStep::Aggregate {{ .. }}"),
351 BuilderStep::Filter { steps, .. } => {
352 write!(f, "BuilderStep::Filter {{ steps: {steps:?}, .. }}")
353 }
354 BuilderStep::Choice { whens, otherwise } => {
355 write!(
356 f,
357 "BuilderStep::Choice {{ whens: {} clause(s), otherwise: {} }}",
358 whens.len(),
359 if otherwise.is_some() { "Some" } else { "None" }
360 )
361 }
362 BuilderStep::WireTap { uri } => write!(f, "BuilderStep::WireTap {{ uri: {uri:?} }}"),
363 BuilderStep::Multicast { steps, .. } => {
364 write!(f, "BuilderStep::Multicast {{ steps: {steps:?}, .. }}")
365 }
366 BuilderStep::DeclarativeLog { level, .. } => {
367 write!(f, "BuilderStep::DeclarativeLog {{ level: {level:?}, .. }}")
368 }
369 BuilderStep::Bean { name, method } => {
370 write!(
371 f,
372 "BuilderStep::Bean {{ name: {name:?}, method: {method:?} }}"
373 )
374 }
375 BuilderStep::Script { language, .. } => {
376 write!(f, "BuilderStep::Script {{ language: {language:?}, .. }}")
377 }
378 BuilderStep::Throttle { steps, .. } => {
379 write!(f, "BuilderStep::Throttle {{ steps: {steps:?}, .. }}")
380 }
381 BuilderStep::LoadBalance { steps, .. } => {
382 write!(f, "BuilderStep::LoadBalance {{ steps: {steps:?}, .. }}")
383 }
384 BuilderStep::DynamicRouter { .. } => {
385 write!(f, "BuilderStep::DynamicRouter {{ .. }}")
386 }
387 BuilderStep::RoutingSlip { .. } => {
388 write!(f, "BuilderStep::RoutingSlip {{ .. }}")
389 }
390 BuilderStep::RecipientList { .. } => {
391 write!(f, "BuilderStep::RecipientList {{ .. }}")
392 }
393 BuilderStep::DeclarativeRecipientList {
394 expression,
395 aggregation,
396 ..
397 } => write!(
398 f,
399 "BuilderStep::DeclarativeRecipientList {{ language: {:?}, aggregation: {:?}, .. }}",
400 expression.language, aggregation
401 ),
402 BuilderStep::Delay { config } => {
403 write!(f, "BuilderStep::Delay {{ config: {:?} }}", config)
404 }
405 BuilderStep::Loop { config, steps } => {
406 write!(
407 f,
408 "BuilderStep::Loop {{ config: {:?}, steps: {} }}",
409 config.mode_name(),
410 steps.len()
411 )
412 }
413 BuilderStep::DeclarativeLoop {
414 count,
415 while_predicate,
416 steps,
417 } => {
418 write!(
419 f,
420 "BuilderStep::DeclarativeLoop {{ count: {:?}, while: {}, steps: {} }}",
421 count,
422 while_predicate.is_some(),
423 steps.len()
424 )
425 }
426 BuilderStep::Validate { predicate } => {
427 write!(
428 f,
429 "BuilderStep::Validate {{ language: {:?}, source: {:?} }}",
430 predicate.language, predicate.source
431 )
432 }
433 BuilderStep::IdempotentConsumer {
434 repository,
435 expression,
436 steps,
437 eager,
438 remove_on_failure,
439 } => {
440 write!(
441 f,
442 "BuilderStep::IdempotentConsumer {{ repository: {repository:?}, language: {:?}, steps: {}, eager: {eager}, remove_on_failure: {remove_on_failure} }}",
443 expression.language,
444 steps.len()
445 )
446 }
447 BuilderStep::Enrich {
448 uri,
449 strategy,
450 timeout_ms,
451 } => {
452 write!(
453 f,
454 "BuilderStep::Enrich {{ uri: {uri:?}, strategy: {strategy:?}, timeout_ms: {timeout_ms:?} }}"
455 )
456 }
457 BuilderStep::PollEnrich {
458 uri,
459 strategy,
460 timeout_ms,
461 } => {
462 write!(
463 f,
464 "BuilderStep::PollEnrich {{ uri: {uri:?}, strategy: {strategy:?}, timeout_ms: {timeout_ms:?} }}"
465 )
466 }
467 BuilderStep::ClaimCheck {
468 repository,
469 operation,
470 key,
471 filter,
472 } => {
473 write!(
474 f,
475 "BuilderStep::ClaimCheck {{ repository: {repository:?}, operation: {operation:?}, \
476 key: {{ language: {:?}, source: {:?} }}, filter: {filter:?} }}",
477 key.language, key.source
478 )
479 }
480 BuilderStep::Sampling { period } => {
481 write!(f, "BuilderStep::Sampling {{ period: {period} }}")
482 }
483 BuilderStep::Sort {
484 expression,
485 reverse,
486 } => {
487 write!(
488 f,
489 "BuilderStep::Sort {{ language: {:?}, source: {:?}, reverse: {reverse} }}",
490 expression.language, expression.source
491 )
492 }
493 BuilderStep::DeclarativeDoTry {
494 try_steps,
495 catch,
496 finally,
497 } => {
498 write!(
499 f,
500 "BuilderStep::DeclarativeDoTry {{ try_steps: {} step(s), catch: {} clause(s), finally: {} }}",
501 try_steps.len(),
502 catch.len(),
503 if finally.is_some() { "Some" } else { "None" }
504 )
505 }
506 BuilderStep::Resequence { .. } => write!(f, "BuilderStep::Resequence {{ .. }}"),
507 }
508 }
509}
510
511pub struct RouteDefinition {
513 pub(crate) from_uri: String,
514 pub(crate) steps: Vec<BuilderStep>,
515 pub(crate) error_handler: Option<ErrorHandlerConfig>,
517 pub(crate) circuit_breaker: Option<CircuitBreakerConfig>,
519 pub(crate) security_policy: Option<SecurityPolicyConfig>,
520 pub(crate) security_authenticator: Option<Arc<dyn TokenAuthenticator>>,
522 pub(crate) unit_of_work: Option<UnitOfWorkConfig>,
524 pub(crate) concurrency: Option<ConcurrencyModel>,
527 pub(crate) route_id: String,
529 pub(crate) auto_startup: bool,
531 pub(crate) startup_order: i32,
533 pub(crate) source_hash: Option<u64>,
534}
535
536impl RouteDefinition {
537 pub fn new(from_uri: impl Into<String>, steps: Vec<BuilderStep>) -> Self {
539 Self {
540 from_uri: from_uri.into(),
541 steps,
542 error_handler: None,
543 circuit_breaker: None,
544 security_policy: None,
545 security_authenticator: None,
546 unit_of_work: None,
547 concurrency: None,
548 route_id: String::new(), auto_startup: true,
550 startup_order: 1000,
551 source_hash: None,
552 }
553 }
554
555 pub fn from_uri(&self) -> &str {
557 &self.from_uri
558 }
559
560 pub fn steps(&self) -> &[BuilderStep] {
562 &self.steps
563 }
564
565 pub fn with_error_handler(mut self, config: ErrorHandlerConfig) -> Self {
567 self.error_handler = Some(config);
568 self
569 }
570
571 pub fn error_handler_config(&self) -> Option<&ErrorHandlerConfig> {
573 self.error_handler.as_ref()
574 }
575
576 pub fn with_circuit_breaker(mut self, config: CircuitBreakerConfig) -> Self {
578 self.circuit_breaker = Some(config);
579 self
580 }
581
582 pub fn with_security_policy(mut self, config: SecurityPolicyConfig) -> Self {
584 self.security_policy = Some(config);
585 self
586 }
587
588 pub fn with_security_authenticator(
590 mut self,
591 authenticator: Arc<dyn TokenAuthenticator>,
592 ) -> Self {
593 self.security_authenticator = Some(authenticator);
594 self
595 }
596
597 pub fn with_unit_of_work(mut self, config: UnitOfWorkConfig) -> Self {
599 self.unit_of_work = Some(config);
600 self
601 }
602
603 pub fn unit_of_work_config(&self) -> Option<&UnitOfWorkConfig> {
605 self.unit_of_work.as_ref()
606 }
607
608 pub fn circuit_breaker_config(&self) -> Option<&CircuitBreakerConfig> {
610 self.circuit_breaker.as_ref()
611 }
612
613 pub fn security_policy_config(&self) -> Option<&SecurityPolicyConfig> {
614 self.security_policy.as_ref()
615 }
616
617 pub fn security_authenticator(&self) -> Option<&Arc<dyn TokenAuthenticator>> {
618 self.security_authenticator.as_ref()
619 }
620
621 pub fn concurrency_override(&self) -> Option<&ConcurrencyModel> {
623 self.concurrency.as_ref()
624 }
625
626 pub fn with_concurrency(mut self, model: ConcurrencyModel) -> Self {
628 self.concurrency = Some(model);
629 self
630 }
631
632 pub fn route_id(&self) -> &str {
634 &self.route_id
635 }
636
637 pub fn auto_startup(&self) -> bool {
639 self.auto_startup
640 }
641
642 pub fn startup_order(&self) -> i32 {
644 self.startup_order
645 }
646
647 pub fn with_route_id(mut self, id: impl Into<String>) -> Self {
649 self.route_id = id.into();
650 self
651 }
652
653 pub fn with_auto_startup(mut self, auto: bool) -> Self {
655 self.auto_startup = auto;
656 self
657 }
658
659 pub fn with_startup_order(mut self, order: i32) -> Self {
661 self.startup_order = order;
662 self
663 }
664
665 pub fn with_source_hash(mut self, hash: u64) -> Self {
666 self.source_hash = Some(hash);
667 self
668 }
669
670 pub fn source_hash(&self) -> Option<u64> {
671 self.source_hash
672 }
673
674 pub fn to_info(&self) -> RouteDefinitionInfo {
677 RouteDefinitionInfo {
678 route_id: self.route_id.clone(),
679 auto_startup: self.auto_startup,
680 startup_order: self.startup_order,
681 source_hash: self.source_hash,
682 }
683 }
684}
685
686#[derive(Clone)]
692pub struct RouteDefinitionInfo {
693 route_id: String,
694 auto_startup: bool,
695 startup_order: i32,
696 pub(crate) source_hash: Option<u64>,
697}
698
699impl RouteDefinitionInfo {
700 pub fn route_id(&self) -> &str {
702 &self.route_id
703 }
704
705 pub fn auto_startup(&self) -> bool {
707 self.auto_startup
708 }
709
710 pub fn startup_order(&self) -> i32 {
712 self.startup_order
713 }
714
715 pub fn source_hash(&self) -> Option<u64> {
716 self.source_hash
717 }
718}
719
720#[cfg(test)]
721mod tests {
722 use super::*;
723
724 #[test]
725 fn test_builder_step_multicast_variant() {
726 use camel_api::MulticastConfig;
727
728 let step = BuilderStep::Multicast {
729 steps: vec![BuilderStep::To("direct:a".into())],
730 config: MulticastConfig::new(),
731 };
732
733 assert!(matches!(step, BuilderStep::Multicast { .. }));
734 }
735
736 #[test]
737 fn test_route_definition_defaults() {
738 let def = RouteDefinition::new("direct:test", vec![]).with_route_id("test-route");
739 assert_eq!(def.route_id(), "test-route");
740 assert!(def.auto_startup());
741 assert_eq!(def.startup_order(), 1000);
742 }
743
744 #[test]
745 fn test_route_definition_builders() {
746 let def = RouteDefinition::new("direct:test", vec![])
747 .with_route_id("my-route")
748 .with_auto_startup(false)
749 .with_startup_order(50);
750 assert_eq!(def.route_id(), "my-route");
751 assert!(!def.auto_startup());
752 assert_eq!(def.startup_order(), 50);
753 }
754
755 #[test]
756 fn test_route_definition_accessors_cover_core_fields() {
757 let def = RouteDefinition::new("direct:in", vec![BuilderStep::To("mock:out".into())])
758 .with_route_id("accessor-route");
759
760 assert_eq!(def.from_uri(), "direct:in");
761 assert_eq!(def.steps().len(), 1);
762 assert!(matches!(def.steps()[0], BuilderStep::To(_)));
763 }
764
765 #[test]
766 fn test_route_definition_error_handler_circuit_breaker_and_concurrency_accessors() {
767 use camel_api::circuit_breaker::CircuitBreakerConfig;
768 use camel_api::error_handler::ErrorHandlerConfig;
769 use camel_component_api::ConcurrencyModel;
770
771 let def = RouteDefinition::new("direct:test", vec![])
772 .with_route_id("eh-route")
773 .with_error_handler(ErrorHandlerConfig::dead_letter_channel("log:dlc"))
774 .with_circuit_breaker(CircuitBreakerConfig::new())
775 .with_concurrency(ConcurrencyModel::Concurrent { max: Some(4) });
776
777 let eh = def
778 .error_handler_config()
779 .expect("error handler should be set");
780 assert_eq!(eh.dlc_uri.as_deref(), Some("log:dlc"));
781 assert!(def.circuit_breaker_config().is_some());
782 assert!(matches!(
783 def.concurrency_override(),
784 Some(ConcurrencyModel::Concurrent { max: Some(4) })
785 ));
786 }
787
788 #[test]
789 fn test_builder_step_debug_covers_many_variants() {
790 use camel_api::splitter::{AggregationStrategy, SplitterConfig, split_body_lines};
791 use camel_api::{
792 DynamicRouterConfig, Exchange, IdentityProcessor, RoutingSlipConfig, Value,
793 };
794 use std::sync::Arc;
795
796 let expr = LanguageExpressionDef {
797 language: "simple".into(),
798 source: "${body}".into(),
799 };
800
801 let steps = vec![
802 BuilderStep::Processor(BoxProcessor::new(IdentityProcessor)),
803 BuilderStep::To("mock:out".into()),
804 BuilderStep::Stop,
805 BuilderStep::Log {
806 level: camel_processor::LogLevel::Info,
807 message: "hello".into(),
808 },
809 BuilderStep::DeclarativeSetHeader {
810 key: "k".into(),
811 value: ValueSourceDef::Literal(Value::String("v".into())),
812 },
813 BuilderStep::DeclarativeSetBody {
814 value: ValueSourceDef::Expression(expr.clone()),
815 },
816 BuilderStep::DeclarativeFilter {
817 predicate: expr.clone(),
818 steps: vec![BuilderStep::Stop],
819 },
820 BuilderStep::DeclarativeChoice {
821 whens: vec![DeclarativeWhenStep {
822 predicate: expr.clone(),
823 steps: vec![BuilderStep::Stop],
824 }],
825 otherwise: Some(vec![BuilderStep::Stop]),
826 },
827 BuilderStep::DeclarativeScript {
828 expression: expr.clone(),
829 },
830 BuilderStep::DeclarativeSplit {
831 expression: expr.clone(),
832 aggregation: AggregationStrategy::Original,
833 parallel: false,
834 parallel_limit: Some(2),
835 stop_on_exception: true,
836 steps: vec![BuilderStep::Stop],
837 },
838 BuilderStep::Split {
839 config: SplitterConfig::new(split_body_lines()),
840 steps: vec![BuilderStep::Stop],
841 },
842 BuilderStep::Aggregate {
843 config: camel_api::AggregatorConfig::correlate_by("id")
844 .complete_when_size(1)
845 .build()
846 .unwrap(),
847 },
848 BuilderStep::Filter {
849 predicate: Arc::new(|_: &Exchange| true),
850 steps: vec![BuilderStep::Stop],
851 },
852 BuilderStep::WireTap {
853 uri: "mock:tap".into(),
854 },
855 BuilderStep::DeclarativeLog {
856 level: camel_processor::LogLevel::Info,
857 message: ValueSourceDef::Expression(expr.clone()),
858 },
859 BuilderStep::Bean {
860 name: "bean".into(),
861 method: "call".into(),
862 },
863 BuilderStep::Script {
864 language: "rhai".into(),
865 script: "body".into(),
866 },
867 BuilderStep::Throttle {
868 config: camel_api::ThrottlerConfig::new(10, std::time::Duration::from_millis(10)),
869 steps: vec![BuilderStep::Stop],
870 },
871 BuilderStep::LoadBalance {
872 config: camel_api::LoadBalancerConfig::round_robin(),
873 steps: vec![BuilderStep::To("mock:l1".into())],
874 },
875 BuilderStep::DynamicRouter {
876 config: DynamicRouterConfig::new(Arc::new(|_| Some("mock:dr".into()))),
877 },
878 BuilderStep::RoutingSlip {
879 config: RoutingSlipConfig::new(Arc::new(|_| Some("mock:rs".into()))),
880 },
881 ];
882
883 for step in steps {
884 let dbg = format!("{step:?}");
885 assert!(!dbg.is_empty());
886 }
887 }
888
889 #[test]
890 fn test_route_definition_to_info_preserves_metadata() {
891 let info = RouteDefinition::new("direct:test", vec![])
892 .with_route_id("meta-route")
893 .with_auto_startup(false)
894 .with_startup_order(7)
895 .to_info();
896
897 assert_eq!(info.route_id(), "meta-route");
898 assert!(!info.auto_startup());
899 assert_eq!(info.startup_order(), 7);
900 }
901
902 #[test]
903 fn test_choice_builder_step_debug() {
904 use camel_api::{Exchange, FilterPredicate};
905 use std::sync::Arc;
906
907 fn always_true(_: &Exchange) -> bool {
908 true
909 }
910
911 let step = BuilderStep::Choice {
912 whens: vec![WhenStep {
913 predicate: Arc::new(always_true) as FilterPredicate,
914 steps: vec![BuilderStep::To("mock:a".into())],
915 }],
916 otherwise: None,
917 };
918 let debug = format!("{step:?}");
919 assert!(debug.contains("Choice"));
920 }
921
922 #[test]
923 fn test_route_definition_unit_of_work() {
924 use camel_api::UnitOfWorkConfig;
925 let config = UnitOfWorkConfig {
926 on_complete: Some("log:complete".into()),
927 on_failure: Some("log:failed".into()),
928 };
929 let def = RouteDefinition::new("direct:test", vec![])
930 .with_route_id("uow-test")
931 .with_unit_of_work(config.clone());
932 assert_eq!(
933 def.unit_of_work_config().unwrap().on_complete.as_deref(),
934 Some("log:complete")
935 );
936 assert_eq!(
937 def.unit_of_work_config().unwrap().on_failure.as_deref(),
938 Some("log:failed")
939 );
940
941 let def_no_uow = RouteDefinition::new("direct:test", vec![]).with_route_id("no-uow");
942 assert!(def_no_uow.unit_of_work_config().is_none());
943 }
944
945 #[test]
946 fn test_route_definition_security_policy_accessor() {
947 use async_trait::async_trait;
948 use camel_api::CamelError;
949 use camel_api::Exchange;
950 use camel_api::security_policy::{
951 AuthorizationDecision, Principal, SecurityPolicy, SecurityPolicyConfig,
952 };
953
954 struct StubPolicy;
955 #[async_trait]
956 impl SecurityPolicy for StubPolicy {
957 async fn evaluate(
958 &self,
959 _exchange: &mut Exchange,
960 ) -> Result<AuthorizationDecision, CamelError> {
961 Ok(AuthorizationDecision::Granted {
962 principal: Principal {
963 subject: "test".into(),
964 issuer: "test".into(),
965 audience: vec![],
966 scopes: vec![],
967 roles: vec![],
968 claims: serde_json::Value::Null,
969 },
970 })
971 }
972 }
973
974 let def_no_sp = RouteDefinition::new("direct:test", vec![]).with_route_id("no-sp");
975 assert!(def_no_sp.security_policy_config().is_none());
976
977 let def = RouteDefinition::new("direct:test", vec![])
978 .with_route_id("sp-test")
979 .with_security_policy(SecurityPolicyConfig::new(StubPolicy));
980 assert!(def.security_policy_config().is_some());
981 }
982
983 #[test]
984 fn test_route_definition_security_authenticator_accessor() {
985 use camel_api::security_policy::Principal;
986
987 struct TestAuth;
988 #[async_trait::async_trait]
989 impl TokenAuthenticator for TestAuth {
990 async fn authenticate_bearer(
991 &self,
992 _token: &str,
993 ) -> Result<Principal, camel_api::CamelError> {
994 Ok(Principal {
995 subject: "test".into(),
996 issuer: "test".into(),
997 audience: vec![],
998 scopes: vec![],
999 roles: vec![],
1000 claims: serde_json::Value::Null,
1001 })
1002 }
1003 }
1004
1005 let def_no_auth = RouteDefinition::new("direct:test".to_string(), vec![]);
1006 assert!(def_no_auth.security_authenticator().is_none());
1007
1008 let auth = Arc::new(TestAuth);
1009 let def = RouteDefinition::new("direct:test".to_string(), vec![])
1010 .with_security_authenticator(auth);
1011 assert!(def.security_authenticator().is_some());
1012 }
1013}