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::{AggregatorConfig, BoxProcessor, FilterPredicate, MulticastConfig, SplitterConfig};
12use camel_auth::TokenAuthenticator;
13use camel_component_api::ConcurrencyModel;
14
15pub struct WhenStep {
17 pub predicate: FilterPredicate,
18 pub steps: Vec<BuilderStep>,
19}
20
21pub use camel_api::declarative::{LanguageExpressionDef, ValueSourceDef};
22
23#[derive(Debug)]
25pub struct DeclarativeWhenStep {
26 pub predicate: LanguageExpressionDef,
27 pub steps: Vec<BuilderStep>,
28}
29
30pub enum BuilderStep {
32 Processor(BoxProcessor),
34 To(String),
36 Stop,
38 Log {
40 level: camel_processor::LogLevel,
41 message: String,
42 },
43 DeclarativeSetHeader {
45 key: String,
46 value: ValueSourceDef,
47 },
48 DeclarativeSetProperty {
49 key: String,
50 value_source: ValueSourceDef,
51 },
52 DeclarativeSetBody {
54 value: ValueSourceDef,
55 },
56 DeclarativeFilter {
58 predicate: LanguageExpressionDef,
59 steps: Vec<BuilderStep>,
60 },
61 DeclarativeChoice {
63 whens: Vec<DeclarativeWhenStep>,
64 otherwise: Option<Vec<BuilderStep>>,
65 },
66 DeclarativeScript {
68 expression: LanguageExpressionDef,
69 },
70 DeclarativeFunction {
71 definition: camel_api::FunctionDefinition,
72 },
73 DeclarativeSplit {
75 expression: LanguageExpressionDef,
76 aggregation: camel_api::splitter::AggregationStrategy,
77 parallel: bool,
78 parallel_limit: Option<usize>,
79 stop_on_exception: bool,
80 steps: Vec<BuilderStep>,
81 },
82 DeclarativeDynamicRouter {
83 expression: LanguageExpressionDef,
84 uri_delimiter: String,
85 cache_size: i32,
86 ignore_invalid_endpoints: bool,
87 max_iterations: usize,
88 },
89 DeclarativeRoutingSlip {
90 expression: LanguageExpressionDef,
91 uri_delimiter: String,
92 cache_size: i32,
93 ignore_invalid_endpoints: bool,
94 },
95 Split {
97 config: SplitterConfig,
98 steps: Vec<BuilderStep>,
99 },
100 Aggregate {
102 config: AggregatorConfig,
103 },
104 Filter {
106 predicate: FilterPredicate,
107 steps: Vec<BuilderStep>,
108 },
109 Choice {
112 whens: Vec<WhenStep>,
113 otherwise: Option<Vec<BuilderStep>>,
114 },
115 WireTap {
117 uri: String,
118 },
119 Multicast {
121 steps: Vec<BuilderStep>,
122 config: MulticastConfig,
123 },
124 DeclarativeLog {
126 level: camel_processor::LogLevel,
127 message: ValueSourceDef,
128 },
129 Bean {
131 name: String,
132 method: String,
133 },
134 Script {
137 language: String,
138 script: String,
139 },
140 Throttle {
142 config: camel_api::ThrottlerConfig,
143 steps: Vec<BuilderStep>,
144 },
145 LoadBalance {
147 config: camel_api::LoadBalancerConfig,
148 steps: Vec<BuilderStep>,
149 },
150 DynamicRouter {
152 config: camel_api::DynamicRouterConfig,
153 },
154 RoutingSlip {
155 config: camel_api::RoutingSlipConfig,
156 },
157 RecipientList {
158 config: camel_api::recipient_list::RecipientListConfig,
159 },
160 DeclarativeRecipientList {
161 expression: LanguageExpressionDef,
162 delimiter: String,
163 parallel: bool,
164 parallel_limit: Option<usize>,
165 stop_on_exception: bool,
166 aggregation: String,
167 },
168 Delay {
169 config: camel_api::DelayConfig,
170 },
171 Loop {
173 config: LoopConfig,
174 steps: Vec<BuilderStep>,
175 },
176 DeclarativeLoop {
178 count: Option<usize>,
179 while_predicate: Option<LanguageExpressionDef>,
180 steps: Vec<BuilderStep>,
181 },
182}
183
184impl std::fmt::Debug for BuilderStep {
185 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
186 match self {
187 BuilderStep::Processor(_) => write!(f, "BuilderStep::Processor(...)"),
188 BuilderStep::To(uri) => write!(f, "BuilderStep::To({uri:?})"),
189 BuilderStep::Stop => write!(f, "BuilderStep::Stop"),
190 BuilderStep::Log { level, message } => write!(
191 f,
192 "BuilderStep::Log {{ level: {level:?}, message: {message:?} }}"
193 ),
194 BuilderStep::DeclarativeSetHeader { key, .. } => {
195 write!(
196 f,
197 "BuilderStep::DeclarativeSetHeader {{ key: {key:?}, .. }}"
198 )
199 }
200 BuilderStep::DeclarativeSetBody { .. } => {
201 write!(f, "BuilderStep::DeclarativeSetBody {{ .. }}")
202 }
203 BuilderStep::DeclarativeSetProperty { key, .. } => {
204 write!(
205 f,
206 "BuilderStep::DeclarativeSetProperty {{ key: {key:?}, .. }}"
207 )
208 }
209 BuilderStep::DeclarativeFilter { steps, .. } => {
210 write!(
211 f,
212 "BuilderStep::DeclarativeFilter {{ steps: {steps:?}, .. }}"
213 )
214 }
215 BuilderStep::DeclarativeChoice { whens, otherwise } => {
216 write!(
217 f,
218 "BuilderStep::DeclarativeChoice {{ whens: {} clause(s), otherwise: {} }}",
219 whens.len(),
220 if otherwise.is_some() { "Some" } else { "None" }
221 )
222 }
223 BuilderStep::DeclarativeScript { expression } => write!(
224 f,
225 "BuilderStep::DeclarativeScript {{ language: {:?}, .. }}",
226 expression.language
227 ),
228 BuilderStep::DeclarativeFunction { definition } => write!(
229 f,
230 "BuilderStep::DeclarativeFunction {{ id: {:?}, runtime: {:?}, .. }}",
231 definition.id, definition.runtime
232 ),
233 BuilderStep::DeclarativeSplit { steps, .. } => {
234 write!(
235 f,
236 "BuilderStep::DeclarativeSplit {{ steps: {steps:?}, .. }}"
237 )
238 }
239 BuilderStep::DeclarativeDynamicRouter { expression, .. } => write!(
240 f,
241 "BuilderStep::DeclarativeDynamicRouter {{ language: {:?}, .. }}",
242 expression.language
243 ),
244 BuilderStep::DeclarativeRoutingSlip { expression, .. } => write!(
245 f,
246 "BuilderStep::DeclarativeRoutingSlip {{ language: {:?}, .. }}",
247 expression.language
248 ),
249 BuilderStep::Split { steps, .. } => {
250 write!(f, "BuilderStep::Split {{ steps: {steps:?}, .. }}")
251 }
252 BuilderStep::Aggregate { .. } => write!(f, "BuilderStep::Aggregate {{ .. }}"),
253 BuilderStep::Filter { steps, .. } => {
254 write!(f, "BuilderStep::Filter {{ steps: {steps:?}, .. }}")
255 }
256 BuilderStep::Choice { whens, otherwise } => {
257 write!(
258 f,
259 "BuilderStep::Choice {{ whens: {} clause(s), otherwise: {} }}",
260 whens.len(),
261 if otherwise.is_some() { "Some" } else { "None" }
262 )
263 }
264 BuilderStep::WireTap { uri } => write!(f, "BuilderStep::WireTap {{ uri: {uri:?} }}"),
265 BuilderStep::Multicast { steps, .. } => {
266 write!(f, "BuilderStep::Multicast {{ steps: {steps:?}, .. }}")
267 }
268 BuilderStep::DeclarativeLog { level, .. } => {
269 write!(f, "BuilderStep::DeclarativeLog {{ level: {level:?}, .. }}")
270 }
271 BuilderStep::Bean { name, method } => {
272 write!(
273 f,
274 "BuilderStep::Bean {{ name: {name:?}, method: {method:?} }}"
275 )
276 }
277 BuilderStep::Script { language, .. } => {
278 write!(f, "BuilderStep::Script {{ language: {language:?}, .. }}")
279 }
280 BuilderStep::Throttle { steps, .. } => {
281 write!(f, "BuilderStep::Throttle {{ steps: {steps:?}, .. }}")
282 }
283 BuilderStep::LoadBalance { steps, .. } => {
284 write!(f, "BuilderStep::LoadBalance {{ steps: {steps:?}, .. }}")
285 }
286 BuilderStep::DynamicRouter { .. } => {
287 write!(f, "BuilderStep::DynamicRouter {{ .. }}")
288 }
289 BuilderStep::RoutingSlip { .. } => {
290 write!(f, "BuilderStep::RoutingSlip {{ .. }}")
291 }
292 BuilderStep::RecipientList { .. } => {
293 write!(f, "BuilderStep::RecipientList {{ .. }}")
294 }
295 BuilderStep::DeclarativeRecipientList {
296 expression,
297 aggregation,
298 ..
299 } => write!(
300 f,
301 "BuilderStep::DeclarativeRecipientList {{ language: {:?}, aggregation: {:?}, .. }}",
302 expression.language, aggregation
303 ),
304 BuilderStep::Delay { config } => {
305 write!(f, "BuilderStep::Delay {{ config: {:?} }}", config)
306 }
307 BuilderStep::Loop { config, steps } => {
308 write!(
309 f,
310 "BuilderStep::Loop {{ config: {:?}, steps: {} }}",
311 config.mode_name(),
312 steps.len()
313 )
314 }
315 BuilderStep::DeclarativeLoop {
316 count,
317 while_predicate,
318 steps,
319 } => {
320 write!(
321 f,
322 "BuilderStep::DeclarativeLoop {{ count: {:?}, while: {}, steps: {} }}",
323 count,
324 while_predicate.is_some(),
325 steps.len()
326 )
327 }
328 }
329 }
330}
331
332pub struct RouteDefinition {
334 pub(crate) from_uri: String,
335 pub(crate) steps: Vec<BuilderStep>,
336 pub(crate) error_handler: Option<ErrorHandlerConfig>,
338 pub(crate) circuit_breaker: Option<CircuitBreakerConfig>,
340 pub(crate) security_policy: Option<SecurityPolicyConfig>,
341 pub(crate) security_authenticator: Option<Arc<dyn TokenAuthenticator>>,
343 pub(crate) unit_of_work: Option<UnitOfWorkConfig>,
345 pub(crate) concurrency: Option<ConcurrencyModel>,
348 pub(crate) route_id: String,
350 pub(crate) auto_startup: bool,
352 pub(crate) startup_order: i32,
354 pub(crate) source_hash: Option<u64>,
355}
356
357impl RouteDefinition {
358 pub fn new(from_uri: impl Into<String>, steps: Vec<BuilderStep>) -> Self {
360 Self {
361 from_uri: from_uri.into(),
362 steps,
363 error_handler: None,
364 circuit_breaker: None,
365 security_policy: None,
366 security_authenticator: None,
367 unit_of_work: None,
368 concurrency: None,
369 route_id: String::new(), auto_startup: true,
371 startup_order: 1000,
372 source_hash: None,
373 }
374 }
375
376 pub fn from_uri(&self) -> &str {
378 &self.from_uri
379 }
380
381 pub fn steps(&self) -> &[BuilderStep] {
383 &self.steps
384 }
385
386 pub fn with_error_handler(mut self, config: ErrorHandlerConfig) -> Self {
388 self.error_handler = Some(config);
389 self
390 }
391
392 pub fn error_handler_config(&self) -> Option<&ErrorHandlerConfig> {
394 self.error_handler.as_ref()
395 }
396
397 pub fn with_circuit_breaker(mut self, config: CircuitBreakerConfig) -> Self {
399 self.circuit_breaker = Some(config);
400 self
401 }
402
403 pub fn with_security_policy(mut self, config: SecurityPolicyConfig) -> Self {
405 self.security_policy = Some(config);
406 self
407 }
408
409 pub fn with_security_authenticator(
411 mut self,
412 authenticator: Arc<dyn TokenAuthenticator>,
413 ) -> Self {
414 self.security_authenticator = Some(authenticator);
415 self
416 }
417
418 pub fn with_unit_of_work(mut self, config: UnitOfWorkConfig) -> Self {
420 self.unit_of_work = Some(config);
421 self
422 }
423
424 pub fn unit_of_work_config(&self) -> Option<&UnitOfWorkConfig> {
426 self.unit_of_work.as_ref()
427 }
428
429 pub fn circuit_breaker_config(&self) -> Option<&CircuitBreakerConfig> {
431 self.circuit_breaker.as_ref()
432 }
433
434 pub fn security_policy_config(&self) -> Option<&SecurityPolicyConfig> {
435 self.security_policy.as_ref()
436 }
437
438 pub fn security_authenticator(&self) -> Option<&Arc<dyn TokenAuthenticator>> {
439 self.security_authenticator.as_ref()
440 }
441
442 pub fn concurrency_override(&self) -> Option<&ConcurrencyModel> {
444 self.concurrency.as_ref()
445 }
446
447 pub fn with_concurrency(mut self, model: ConcurrencyModel) -> Self {
449 self.concurrency = Some(model);
450 self
451 }
452
453 pub fn route_id(&self) -> &str {
455 &self.route_id
456 }
457
458 pub fn auto_startup(&self) -> bool {
460 self.auto_startup
461 }
462
463 pub fn startup_order(&self) -> i32 {
465 self.startup_order
466 }
467
468 pub fn with_route_id(mut self, id: impl Into<String>) -> Self {
470 self.route_id = id.into();
471 self
472 }
473
474 pub fn with_auto_startup(mut self, auto: bool) -> Self {
476 self.auto_startup = auto;
477 self
478 }
479
480 pub fn with_startup_order(mut self, order: i32) -> Self {
482 self.startup_order = order;
483 self
484 }
485
486 pub fn with_source_hash(mut self, hash: u64) -> Self {
487 self.source_hash = Some(hash);
488 self
489 }
490
491 pub fn source_hash(&self) -> Option<u64> {
492 self.source_hash
493 }
494
495 pub fn to_info(&self) -> RouteDefinitionInfo {
498 RouteDefinitionInfo {
499 route_id: self.route_id.clone(),
500 auto_startup: self.auto_startup,
501 startup_order: self.startup_order,
502 source_hash: self.source_hash,
503 }
504 }
505}
506
507#[derive(Clone)]
513pub struct RouteDefinitionInfo {
514 route_id: String,
515 auto_startup: bool,
516 startup_order: i32,
517 pub(crate) source_hash: Option<u64>,
518}
519
520impl RouteDefinitionInfo {
521 pub fn route_id(&self) -> &str {
523 &self.route_id
524 }
525
526 pub fn auto_startup(&self) -> bool {
528 self.auto_startup
529 }
530
531 pub fn startup_order(&self) -> i32 {
533 self.startup_order
534 }
535
536 pub fn source_hash(&self) -> Option<u64> {
537 self.source_hash
538 }
539}
540
541#[cfg(test)]
542mod tests {
543 use super::*;
544
545 #[test]
546 fn test_builder_step_multicast_variant() {
547 use camel_api::MulticastConfig;
548
549 let step = BuilderStep::Multicast {
550 steps: vec![BuilderStep::To("direct:a".into())],
551 config: MulticastConfig::new(),
552 };
553
554 assert!(matches!(step, BuilderStep::Multicast { .. }));
555 }
556
557 #[test]
558 fn test_route_definition_defaults() {
559 let def = RouteDefinition::new("direct:test", vec![]).with_route_id("test-route");
560 assert_eq!(def.route_id(), "test-route");
561 assert!(def.auto_startup());
562 assert_eq!(def.startup_order(), 1000);
563 }
564
565 #[test]
566 fn test_route_definition_builders() {
567 let def = RouteDefinition::new("direct:test", vec![])
568 .with_route_id("my-route")
569 .with_auto_startup(false)
570 .with_startup_order(50);
571 assert_eq!(def.route_id(), "my-route");
572 assert!(!def.auto_startup());
573 assert_eq!(def.startup_order(), 50);
574 }
575
576 #[test]
577 fn test_route_definition_accessors_cover_core_fields() {
578 let def = RouteDefinition::new("direct:in", vec![BuilderStep::To("mock:out".into())])
579 .with_route_id("accessor-route");
580
581 assert_eq!(def.from_uri(), "direct:in");
582 assert_eq!(def.steps().len(), 1);
583 assert!(matches!(def.steps()[0], BuilderStep::To(_)));
584 }
585
586 #[test]
587 fn test_route_definition_error_handler_circuit_breaker_and_concurrency_accessors() {
588 use camel_api::circuit_breaker::CircuitBreakerConfig;
589 use camel_api::error_handler::ErrorHandlerConfig;
590 use camel_component_api::ConcurrencyModel;
591
592 let def = RouteDefinition::new("direct:test", vec![])
593 .with_route_id("eh-route")
594 .with_error_handler(ErrorHandlerConfig::dead_letter_channel("log:dlc"))
595 .with_circuit_breaker(CircuitBreakerConfig::new())
596 .with_concurrency(ConcurrencyModel::Concurrent { max: Some(4) });
597
598 let eh = def
599 .error_handler_config()
600 .expect("error handler should be set");
601 assert_eq!(eh.dlc_uri.as_deref(), Some("log:dlc"));
602 assert!(def.circuit_breaker_config().is_some());
603 assert!(matches!(
604 def.concurrency_override(),
605 Some(ConcurrencyModel::Concurrent { max: Some(4) })
606 ));
607 }
608
609 #[test]
610 fn test_builder_step_debug_covers_many_variants() {
611 use camel_api::splitter::{AggregationStrategy, SplitterConfig, split_body_lines};
612 use camel_api::{
613 DynamicRouterConfig, Exchange, IdentityProcessor, RoutingSlipConfig, Value,
614 };
615 use std::sync::Arc;
616
617 let expr = LanguageExpressionDef {
618 language: "simple".into(),
619 source: "${body}".into(),
620 };
621
622 let steps = vec![
623 BuilderStep::Processor(BoxProcessor::new(IdentityProcessor)),
624 BuilderStep::To("mock:out".into()),
625 BuilderStep::Stop,
626 BuilderStep::Log {
627 level: camel_processor::LogLevel::Info,
628 message: "hello".into(),
629 },
630 BuilderStep::DeclarativeSetHeader {
631 key: "k".into(),
632 value: ValueSourceDef::Literal(Value::String("v".into())),
633 },
634 BuilderStep::DeclarativeSetBody {
635 value: ValueSourceDef::Expression(expr.clone()),
636 },
637 BuilderStep::DeclarativeFilter {
638 predicate: expr.clone(),
639 steps: vec![BuilderStep::Stop],
640 },
641 BuilderStep::DeclarativeChoice {
642 whens: vec![DeclarativeWhenStep {
643 predicate: expr.clone(),
644 steps: vec![BuilderStep::Stop],
645 }],
646 otherwise: Some(vec![BuilderStep::Stop]),
647 },
648 BuilderStep::DeclarativeScript {
649 expression: expr.clone(),
650 },
651 BuilderStep::DeclarativeSplit {
652 expression: expr.clone(),
653 aggregation: AggregationStrategy::Original,
654 parallel: false,
655 parallel_limit: Some(2),
656 stop_on_exception: true,
657 steps: vec![BuilderStep::Stop],
658 },
659 BuilderStep::Split {
660 config: SplitterConfig::new(split_body_lines()),
661 steps: vec![BuilderStep::Stop],
662 },
663 BuilderStep::Aggregate {
664 config: camel_api::AggregatorConfig::correlate_by("id")
665 .complete_when_size(1)
666 .build()
667 .unwrap(),
668 },
669 BuilderStep::Filter {
670 predicate: Arc::new(|_: &Exchange| true),
671 steps: vec![BuilderStep::Stop],
672 },
673 BuilderStep::WireTap {
674 uri: "mock:tap".into(),
675 },
676 BuilderStep::DeclarativeLog {
677 level: camel_processor::LogLevel::Info,
678 message: ValueSourceDef::Expression(expr.clone()),
679 },
680 BuilderStep::Bean {
681 name: "bean".into(),
682 method: "call".into(),
683 },
684 BuilderStep::Script {
685 language: "rhai".into(),
686 script: "body".into(),
687 },
688 BuilderStep::Throttle {
689 config: camel_api::ThrottlerConfig::new(10, std::time::Duration::from_millis(10)),
690 steps: vec![BuilderStep::Stop],
691 },
692 BuilderStep::LoadBalance {
693 config: camel_api::LoadBalancerConfig::round_robin(),
694 steps: vec![BuilderStep::To("mock:l1".into())],
695 },
696 BuilderStep::DynamicRouter {
697 config: DynamicRouterConfig::new(Arc::new(|_| Some("mock:dr".into()))),
698 },
699 BuilderStep::RoutingSlip {
700 config: RoutingSlipConfig::new(Arc::new(|_| Some("mock:rs".into()))),
701 },
702 ];
703
704 for step in steps {
705 let dbg = format!("{step:?}");
706 assert!(!dbg.is_empty());
707 }
708 }
709
710 #[test]
711 fn test_route_definition_to_info_preserves_metadata() {
712 let info = RouteDefinition::new("direct:test", vec![])
713 .with_route_id("meta-route")
714 .with_auto_startup(false)
715 .with_startup_order(7)
716 .to_info();
717
718 assert_eq!(info.route_id(), "meta-route");
719 assert!(!info.auto_startup());
720 assert_eq!(info.startup_order(), 7);
721 }
722
723 #[test]
724 fn test_choice_builder_step_debug() {
725 use camel_api::{Exchange, FilterPredicate};
726 use std::sync::Arc;
727
728 fn always_true(_: &Exchange) -> bool {
729 true
730 }
731
732 let step = BuilderStep::Choice {
733 whens: vec![WhenStep {
734 predicate: Arc::new(always_true) as FilterPredicate,
735 steps: vec![BuilderStep::To("mock:a".into())],
736 }],
737 otherwise: None,
738 };
739 let debug = format!("{step:?}");
740 assert!(debug.contains("Choice"));
741 }
742
743 #[test]
744 fn test_route_definition_unit_of_work() {
745 use camel_api::UnitOfWorkConfig;
746 let config = UnitOfWorkConfig {
747 on_complete: Some("log:complete".into()),
748 on_failure: Some("log:failed".into()),
749 };
750 let def = RouteDefinition::new("direct:test", vec![])
751 .with_route_id("uow-test")
752 .with_unit_of_work(config.clone());
753 assert_eq!(
754 def.unit_of_work_config().unwrap().on_complete.as_deref(),
755 Some("log:complete")
756 );
757 assert_eq!(
758 def.unit_of_work_config().unwrap().on_failure.as_deref(),
759 Some("log:failed")
760 );
761
762 let def_no_uow = RouteDefinition::new("direct:test", vec![]).with_route_id("no-uow");
763 assert!(def_no_uow.unit_of_work_config().is_none());
764 }
765
766 #[test]
767 fn test_route_definition_security_policy_accessor() {
768 use async_trait::async_trait;
769 use camel_api::CamelError;
770 use camel_api::Exchange;
771 use camel_api::security_policy::{
772 AuthorizationDecision, Principal, SecurityPolicy, SecurityPolicyConfig,
773 };
774
775 struct StubPolicy;
776 #[async_trait]
777 impl SecurityPolicy for StubPolicy {
778 async fn evaluate(
779 &self,
780 _exchange: &mut Exchange,
781 ) -> Result<AuthorizationDecision, CamelError> {
782 Ok(AuthorizationDecision::Granted {
783 principal: Principal {
784 subject: "test".into(),
785 issuer: "test".into(),
786 audience: vec![],
787 scopes: vec![],
788 roles: vec![],
789 claims: serde_json::Value::Null,
790 },
791 })
792 }
793 }
794
795 let def_no_sp = RouteDefinition::new("direct:test", vec![]).with_route_id("no-sp");
796 assert!(def_no_sp.security_policy_config().is_none());
797
798 let def = RouteDefinition::new("direct:test", vec![])
799 .with_route_id("sp-test")
800 .with_security_policy(SecurityPolicyConfig::new(StubPolicy));
801 assert!(def.security_policy_config().is_some());
802 }
803
804 #[test]
805 fn test_route_definition_security_authenticator_accessor() {
806 use camel_api::security_policy::Principal;
807
808 struct TestAuth;
809 #[async_trait::async_trait]
810 impl TokenAuthenticator for TestAuth {
811 async fn authenticate_bearer(
812 &self,
813 _token: &str,
814 ) -> Result<Principal, camel_api::CamelError> {
815 Ok(Principal {
816 subject: "test".into(),
817 issuer: "test".into(),
818 audience: vec![],
819 scopes: vec![],
820 roles: vec![],
821 claims: serde_json::Value::Null,
822 })
823 }
824 }
825
826 let def_no_auth = RouteDefinition::new("direct:test".to_string(), vec![]);
827 assert!(def_no_auth.security_authenticator().is_none());
828
829 let auth = Arc::new(TestAuth);
830 let def = RouteDefinition::new("direct:test".to_string(), vec![])
831 .with_security_authenticator(auth);
832 assert!(def.security_authenticator().is_some());
833 }
834}