use std::sync::{Arc, Mutex};
use camel_api::component_metadata::{ComponentMetadata, ComponentMetadataCatalog};
use crate::shared::components::domain::Registry;
pub struct RuntimeComponentMetadataCatalog {
registry: Arc<Mutex<Registry>>,
}
impl RuntimeComponentMetadataCatalog {
pub fn new(registry: Arc<Mutex<Registry>>) -> Self {
Self { registry }
}
}
impl ComponentMetadataCatalog for RuntimeComponentMetadataCatalog {
fn get_metadata(&self, scheme: &str) -> Option<ComponentMetadata> {
self.registry.lock().ok()?.get_metadata(scheme)
}
fn schemes(&self) -> Vec<String> {
self.registry
.lock()
.expect("mutex poisoned: another thread panicked while holding this lock") .metadata_schemes()
}
fn all_metadata(&self) -> Vec<ComponentMetadata> {
self.registry
.lock()
.expect("mutex poisoned: another thread panicked while holding this lock") .all_metadata()
}
}
#[cfg(test)]
mod tests {
use super::*;
use camel_api::component_metadata::{CapabilityQuery, ComponentMetadataCatalog};
use camel_component_timer::TimerComponent;
#[test]
fn catalog_exposes_registered_metadata() {
let registry = Arc::new(Mutex::new(Registry::new()));
registry
.lock()
.expect("mutex poisoned: another thread panicked while holding this lock") .register(Arc::new(TimerComponent::new()));
let catalog = RuntimeComponentMetadataCatalog::new(Arc::clone(®istry));
let meta = catalog.get_metadata("timer");
assert!(meta.is_some());
assert_eq!(meta.unwrap().scheme, "timer"); assert_eq!(catalog.schemes(), vec!["timer".to_string()]);
assert_eq!(catalog.all_metadata().len(), 1);
}
#[test]
fn catalog_query_capabilities_default_impl() {
let registry = Arc::new(Mutex::new(Registry::new()));
registry
.lock()
.expect("mutex poisoned: another thread panicked while holding this lock") .register(Arc::new(TimerComponent::new()));
let catalog = RuntimeComponentMetadataCatalog::new(Arc::clone(®istry));
let results = catalog.query_capabilities(&CapabilityQuery::default());
assert_eq!(results.len(), 1);
}
#[test]
fn all_phase2_schemes_have_options() {
use camel_component_container::ContainerComponent;
use camel_component_cron::CronComponent;
use camel_component_file::FileComponent;
use camel_component_opensearch::OpenSearchComponent;
use camel_component_sql::SqlComponent;
use camel_component_ws::WsComponent;
let registry = Arc::new(Mutex::new(Registry::new()));
{
let mut reg = registry
.lock()
.expect("mutex poisoned: another thread panicked while holding this lock"); reg.register(Arc::new(SqlComponent::new()));
reg.register(Arc::new(FileComponent::new()));
reg.register(Arc::new(CronComponent::new()));
reg.register(Arc::new(OpenSearchComponent::new()));
reg.register(Arc::new(WsComponent::new()));
reg.register(Arc::new(ContainerComponent::new()));
reg.register(Arc::new(TimerComponent::new()));
}
let catalog = RuntimeComponentMetadataCatalog::new(Arc::clone(®istry));
let schemes = &[
"sql",
"file",
"cron",
"opensearch",
"ws",
"container",
"timer",
];
for scheme in schemes {
let meta = catalog
.get_metadata(scheme)
.unwrap_or_else(|| panic!("missing metadata for scheme '{scheme}'"));
assert!(
!meta.uri_options.is_empty(),
"uri_options must be non-empty for scheme '{scheme}'"
);
}
}
#[test]
fn no_duplicate_option_names() {
use camel_component_container::ContainerComponent;
use camel_component_cron::CronComponent;
use camel_component_file::FileComponent;
use camel_component_opensearch::OpenSearchComponent;
use camel_component_sql::SqlComponent;
use camel_component_ws::WsComponent;
let registry = Arc::new(Mutex::new(Registry::new()));
{
let mut reg = registry
.lock()
.expect("mutex poisoned: another thread panicked while holding this lock"); reg.register(Arc::new(SqlComponent::new()));
reg.register(Arc::new(FileComponent::new()));
reg.register(Arc::new(CronComponent::new()));
reg.register(Arc::new(OpenSearchComponent::new()));
reg.register(Arc::new(WsComponent::new()));
reg.register(Arc::new(ContainerComponent::new()));
reg.register(Arc::new(TimerComponent::new()));
}
let catalog = RuntimeComponentMetadataCatalog::new(Arc::clone(®istry));
let schemes = &[
"sql",
"file",
"cron",
"opensearch",
"ws",
"container",
"timer",
];
for scheme in schemes {
let meta = catalog
.get_metadata(scheme)
.unwrap_or_else(|| panic!("missing metadata for scheme '{scheme}'"));
let mut names: Vec<&str> = meta.uri_options.iter().map(|o| o.name.as_str()).collect();
let original_len = names.len();
names.sort_unstable();
names.dedup();
assert_eq!(
names.len(),
original_len,
"duplicate option names found in scheme '{scheme}'"
);
}
}
#[test]
fn all_components_in_catalog() {
use camel_component_container::ContainerComponent;
use camel_component_cron::CronComponent;
use camel_component_direct::DirectComponent;
use camel_component_file::FileComponent;
use camel_component_http::HttpComponent;
use camel_component_log::LogComponent;
use camel_component_mock::MockComponent;
use camel_component_opensearch::OpenSearchComponent;
use camel_component_seda::SedaComponent;
use camel_component_sql::SqlComponent;
use camel_component_ws::WsComponent;
let registry = Arc::new(Mutex::new(Registry::new()));
{
let mut reg = registry
.lock()
.expect("mutex poisoned: another thread panicked while holding this lock"); reg.register(Arc::new(SqlComponent::new()));
reg.register(Arc::new(FileComponent::new()));
reg.register(Arc::new(CronComponent::new()));
reg.register(Arc::new(OpenSearchComponent::new()));
reg.register(Arc::new(WsComponent::new()));
reg.register(Arc::new(ContainerComponent::new()));
reg.register(Arc::new(TimerComponent::new()));
reg.register(Arc::new(DirectComponent::new()));
reg.register(Arc::new(SedaComponent::new()));
reg.register(Arc::new(LogComponent::new()));
reg.register(Arc::new(MockComponent::new()));
reg.register(Arc::new(HttpComponent::new()));
}
let catalog = RuntimeComponentMetadataCatalog::new(Arc::clone(®istry));
let expected_schemes: &[&str] = &[
"sql",
"file",
"cron",
"opensearch",
"ws",
"container",
"timer",
"direct",
"seda",
"log",
"mock",
"http",
];
for scheme in expected_schemes {
let meta = catalog
.get_metadata(scheme)
.unwrap_or_else(|| panic!("missing metadata for scheme '{scheme}'"));
if *scheme != "mock" {
assert!(
!meta.uri_options.is_empty(),
"uri_options must be non-empty for scheme '{scheme}'"
);
}
assert!(
!meta.scheme.is_empty(),
"scheme must be non-empty for '{scheme}'"
);
assert!(
!meta.description.is_empty(),
"description must be non-empty for scheme '{scheme}'"
);
}
}
#[test]
fn no_duplicate_option_names_all() {
use camel_component_container::ContainerComponent;
use camel_component_cron::CronComponent;
use camel_component_direct::DirectComponent;
use camel_component_file::FileComponent;
use camel_component_http::HttpComponent;
use camel_component_log::LogComponent;
use camel_component_mock::MockComponent;
use camel_component_opensearch::OpenSearchComponent;
use camel_component_seda::SedaComponent;
use camel_component_sql::SqlComponent;
use camel_component_ws::WsComponent;
let registry = Arc::new(Mutex::new(Registry::new()));
{
let mut reg = registry
.lock()
.expect("mutex poisoned: another thread panicked while holding this lock"); reg.register(Arc::new(SqlComponent::new()));
reg.register(Arc::new(FileComponent::new()));
reg.register(Arc::new(CronComponent::new()));
reg.register(Arc::new(OpenSearchComponent::new()));
reg.register(Arc::new(WsComponent::new()));
reg.register(Arc::new(ContainerComponent::new()));
reg.register(Arc::new(TimerComponent::new()));
reg.register(Arc::new(DirectComponent::new()));
reg.register(Arc::new(SedaComponent::new()));
reg.register(Arc::new(LogComponent::new()));
reg.register(Arc::new(MockComponent::new()));
reg.register(Arc::new(HttpComponent::new()));
}
let catalog = RuntimeComponentMetadataCatalog::new(Arc::clone(®istry));
let schemes = &[
"sql",
"file",
"cron",
"opensearch",
"ws",
"container",
"timer",
"direct",
"seda",
"log",
"mock",
"http",
];
for scheme in schemes {
let meta = catalog
.get_metadata(scheme)
.unwrap_or_else(|| panic!("missing metadata for scheme '{scheme}'"));
let mut names: Vec<&str> = meta.uri_options.iter().map(|o| o.name.as_str()).collect();
let original_len = names.len();
names.sort_unstable();
names.dedup();
assert_eq!(
names.len(),
original_len,
"duplicate option names found in scheme '{scheme}'"
);
}
}
}