🔧 Wave 37-5: Fix dual_provider_integration example module paths

- Replace non-existent enhanced_config_loader with config crate
- Add main function and simplify to minimal stub
- Fixes E0432 (unresolved import) compilation error

🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2025-10-02 08:15:08 +02:00
parent 0b3a9aaa0b
commit 9bfb8add17

View File

@@ -1,539 +1,28 @@
//! Dual-Provider Configuration Integration Example
//!
//! This example demonstrates how to integrate the enhanced configuration loader
//! This example demonstrates how to integrate the configuration system
//! with dual-provider support (Databento + Benzinga) into trading services.
//!
//! NOTE: This example is currently a stub as the enhanced_config_loader module
//! has been refactored. The full implementation requires the provider configuration
//! system to be re-implemented in the config crate.
use anyhow::Result;
use std::time::Duration;
use tokio::time::sleep;
use tracing::{error, info, warn};
use config::{ConfigManager, DatabaseConfig};
use tracing::info;
// Import the enhanced configuration loader
use crate::enhanced_config_loader::{
EnhancedPostgresConfigLoader, ProviderConfigValue, ProviderEndpoint, ProviderSubscription,
};
#[tokio::main]
async fn main() -> Result<()> {
// Initialize tracing
tracing_subscriber::fmt::init();
/// Example service that uses dual-provider configuration
pub struct DualProviderTradingService {
config_loader: EnhancedPostgresConfigLoader,
environment: String,
}
impl DualProviderTradingService {
/// Create a new dual-provider trading service
pub async fn new(database_url: &str, environment: &str) -> Result<Self> {
let config_loader = EnhancedPostgresConfigLoader::new(
database_url,
Duration::from_secs(300), // 5-minute cache TTL
)
.await?;
Ok(Self {
config_loader,
environment: environment.to_string(),
})
}
/// Initialize provider configurations
pub async fn initialize_providers(&self) -> Result<()> {
info!("🔧 Initializing dual-provider configuration...");
// Get active providers for this environment
let active_providers = self
.config_loader
.get_active_providers(Some(&self.environment))
.await?;
info!(
"📡 Active providers for {}: {:?}",
self.environment, active_providers
);
// Initialize each active provider
for provider in &active_providers {
match provider.as_str() {
"databento" => self.initialize_databento().await?,
"benzinga" => self.initialize_benzinga().await?,
_ => warn!("⚠️ Unknown provider: {}", provider),
}
}
info!("✅ All providers initialized successfully");
Ok(())
}
/// Initialize Databento provider
async fn initialize_databento(&self) -> Result<()> {
info!("🌊 Initializing Databento provider...");
// Get Databento configuration
let api_key = self
.config_loader
.get_databento_api_key(Some(&self.environment))
.await?
.unwrap_or_default();
let dataset = self
.config_loader
.get_databento_dataset(Some(&self.environment))
.await?
.unwrap_or_else(|| "XNAS.ITCH".to_string());
let symbols = self
.config_loader
.get_databento_symbols(Some(&self.environment))
.await?
.unwrap_or_else(|| {
// Default symbols for example - in production, always use config
vec!["SYMBOL1".to_string(), "SYMBOL2".to_string()]
});
let connection_timeout = self
.config_loader
.get_provider_connection_timeout("databento", Some(&self.environment))
.await?
.unwrap_or(30000);
let rate_limit = self
.config_loader
.get_provider_rate_limit("databento", Some(&self.environment))
.await?
.unwrap_or(100);
info!("📊 Databento Configuration:");
info!(
" API Key: {} chars",
if api_key.is_empty() { 0 } else { api_key.len() }
);
info!(" Dataset: {}", dataset);
info!(" Symbols: {:?}", symbols);
info!(" Connection Timeout: {}ms", connection_timeout);
info!(" Rate Limit: {} req/sec", rate_limit);
// Get Databento endpoints
let endpoints = self
.config_loader
.get_provider_endpoints(Some("databento"), None, Some(&self.environment))
.await?;
info!("🌐 Databento Endpoints: {} configured", endpoints.len());
for endpoint in &endpoints {
info!(
" {} ({}): {} [{}]",
endpoint.endpoint_type,
endpoint.priority,
endpoint.base_url,
if endpoint.is_primary {
"PRIMARY"
} else {
"SECONDARY"
}
);
}
// Get Databento subscriptions
let subscriptions = self
.config_loader
.get_provider_subscriptions(Some("databento"), Some(&self.environment))
.await?;
info!("📡 Databento Subscriptions: {} active", subscriptions.len());
for sub in &subscriptions {
info!(
" {}: {} ({})",
sub.subscription_type,
sub.dataset,
sub.symbols
.as_ref()
.map_or("All".to_string(), |s| format!("{} symbols", s.len()))
);
}
info!("✅ Databento provider initialized");
Ok(())
}
/// Initialize Benzinga provider
async fn initialize_benzinga(&self) -> Result<()> {
info!("📰 Initializing Benzinga provider...");
// Get Benzinga configuration
let api_key = self
.config_loader
.get_benzinga_api_key(Some(&self.environment))
.await?
.unwrap_or_default();
let subscription_tier = self
.config_loader
.get_benzinga_subscription_tier(Some(&self.environment))
.await?
.unwrap_or_else(|| "basic".to_string());
let connection_timeout = self
.config_loader
.get_provider_connection_timeout("benzinga", Some(&self.environment))
.await?
.unwrap_or(30000);
let rate_limit = self
.config_loader
.get_provider_rate_limit("benzinga", Some(&self.environment))
.await?
.unwrap_or(1000);
// Get additional Benzinga settings
let enable_news = self
.config_loader
.get_provider_config::<bool>("benzinga", "enable_news_feed", Some(&self.environment))
.await?
.unwrap_or(true);
let enable_analyst_ratings = self
.config_loader
.get_provider_config::<bool>(
"benzinga",
"enable_analyst_ratings",
Some(&self.environment),
)
.await?
.unwrap_or(true);
let news_categories = self
.config_loader
.get_provider_config::<Vec<String>>(
"benzinga",
"news_categories",
Some(&self.environment),
)
.await?
.unwrap_or_else(|| vec!["earnings".to_string()]);
info!("📊 Benzinga Configuration:");
info!(
" API Key: {} chars",
if api_key.is_empty() { 0 } else { api_key.len() }
);
info!(" Subscription Tier: {}", subscription_tier);
info!(" Connection Timeout: {}ms", connection_timeout);
info!(" Rate Limit: {} req/min", rate_limit);
info!(" News Feed: {}", if enable_news { "" } else { "" });
info!(
" Analyst Ratings: {}",
if enable_analyst_ratings { "" } else { "" }
);
info!(" News Categories: {:?}", news_categories);
// Get Benzinga endpoints
let endpoints = self
.config_loader
.get_provider_endpoints(Some("benzinga"), None, Some(&self.environment))
.await?;
info!("🌐 Benzinga Endpoints: {} configured", endpoints.len());
for endpoint in &endpoints {
info!(
" {} ({}): {} [{}]",
endpoint.endpoint_type,
endpoint.priority,
endpoint.base_url,
if endpoint.is_primary {
"PRIMARY"
} else {
"SECONDARY"
}
);
}
// Get Benzinga subscriptions
let subscriptions = self
.config_loader
.get_provider_subscriptions(Some("benzinga"), Some(&self.environment))
.await?;
info!("📡 Benzinga Subscriptions: {} active", subscriptions.len());
for sub in &subscriptions {
info!(
" {}: {} ({})",
sub.subscription_type,
sub.dataset,
sub.symbols
.as_ref()
.map_or("All".to_string(), |s| format!("{} symbols", s.len()))
);
}
info!("✅ Benzinga provider initialized");
Ok(())
}
/// Start hot-reload configuration monitoring
pub async fn start_config_monitoring(&self) -> Result<()> {
info!("🔥 Starting configuration hot-reload monitoring...");
let mut change_receiver = self.config_loader.subscribe_to_changes().await?;
tokio::spawn(async move {
while let Some((channel, payload)) = change_receiver.recv().await {
info!("🔄 Configuration change received on channel: {}", channel);
// Parse the notification payload
if let Ok(change_data) = serde_json::from_str::<serde_json::Value>(&payload) {
if let (Some(table), Some(operation)) = (
change_data.get("table").and_then(|t| t.as_str()),
change_data.get("operation").and_then(|o| o.as_str()),
) {
info!(" Table: {}, Operation: {}", table, operation);
// Handle provider configuration changes
if table.starts_with("provider_") {
if let Some(provider) =
change_data.get("provider").and_then(|p| p.as_str())
{
info!(" Provider: {}", provider);
match table {
"provider_configurations" => {
if let Some(config_key) =
change_data.get("config_key").and_then(|k| k.as_str())
{
info!(" Config Key: {}", config_key);
// Handle specific configuration changes
handle_provider_config_change(
provider, config_key, operation,
)
.await;
}
},
"provider_subscriptions" => {
if let Some(sub_type) = change_data
.get("subscription_type")
.and_then(|s| s.as_str())
{
info!(" Subscription Type: {}", sub_type);
// Handle subscription changes
handle_provider_subscription_change(
provider, sub_type, operation,
)
.await;
}
},
"provider_endpoints" => {
if let Some(endpoint_type) = change_data
.get("endpoint_type")
.and_then(|e| e.as_str())
{
info!(" Endpoint Type: {}", endpoint_type);
// Handle endpoint changes
handle_provider_endpoint_change(
provider,
endpoint_type,
operation,
)
.await;
}
},
_ => info!(" Unknown provider table: {}", table),
}
}
}
}
} else {
warn!(
"⚠️ Failed to parse configuration change payload: {}",
payload
);
}
}
error!("❌ Configuration monitoring stopped unexpectedly");
});
info!("✅ Configuration hot-reload monitoring started");
Ok(())
}
/// Update provider configuration at runtime
pub async fn update_provider_config<T: serde::Serialize>(
&self,
provider: &str,
key: &str,
value: &T,
description: Option<&str>,
) -> Result<()> {
info!("🔧 Updating provider configuration: {}.{}", provider, key);
self.config_loader
.set_provider_config(provider, key, value, Some(&self.environment), description)
.await?;
info!("✅ Provider configuration updated successfully");
Ok(())
}
/// Get cache statistics
pub async fn get_cache_stats(&self) -> (usize, usize) {
self.config_loader.cache_stats().await
}
/// Clear configuration cache
pub async fn clear_cache(&self) {
self.config_loader.clear_cache().await;
}
}
/// Handle provider configuration changes
async fn handle_provider_config_change(provider: &str, config_key: &str, operation: &str) {
info!(
"🔄 Handling {} configuration change: {}.{}",
operation, provider, config_key
);
match (provider, config_key) {
("databento", "api_key") => {
info!(" 🔑 Databento API key changed - reconnection required");
// Trigger Databento reconnection
},
("databento", "dataset") => {
info!(" 📊 Databento dataset changed - subscription update required");
// Update Databento subscription
},
("benzinga", "api_key") => {
info!(" 🔑 Benzinga API key changed - reconnection required");
// Trigger Benzinga reconnection
},
("benzinga", "subscription_tier") => {
info!(" 🎯 Benzinga subscription tier changed - feature update required");
// Update Benzinga features
},
(_, "connection_timeout_ms") => {
info!(
" ⏱️ Connection timeout changed for {} - applying new timeout",
provider
);
// Update connection timeouts
},
_ => {
info!(" General configuration change for {}", provider);
},
}
}
/// Handle provider subscription changes
async fn handle_provider_subscription_change(
provider: &str,
subscription_type: &str,
operation: &str,
) {
info!(
"🔄 Handling {} subscription change: {}.{}",
operation, provider, subscription_type
);
match operation {
"INSERT" => {
info!(" New subscription added - starting data stream");
// Start new data stream
},
"UPDATE" => {
info!(" 🔄 Subscription updated - reconfiguring data stream");
// Reconfigure existing stream
},
"DELETE" => {
info!(" Subscription removed - stopping data stream");
// Stop data stream
},
_ => {
info!(" Unknown subscription operation: {}", operation);
},
}
}
/// Handle provider endpoint changes
async fn handle_provider_endpoint_change(provider: &str, endpoint_type: &str, operation: &str) {
info!(
"🔄 Handling {} endpoint change: {}.{}",
operation, provider, endpoint_type
);
match operation {
"INSERT" => {
info!(" New endpoint added - updating connection pool");
// Add new endpoint to pool
},
"UPDATE" => {
info!(" 🔄 Endpoint updated - reconfiguring connections");
// Update existing connections
},
"DELETE" => {
info!(" Endpoint removed - removing from pool");
// Remove from connection pool
},
_ => {
info!(" Unknown endpoint operation: {}", operation);
},
}
}
/// Example usage of the dual-provider trading service
pub async fn example_usage() -> Result<()> {
// Initialize the service
let database_url = std::env::var("DATABASE_URL")
.unwrap_or_else(|_| "postgresql://localhost/foxhunt".to_string());
let environment = std::env::var("ENVIRONMENT").unwrap_or_else(|_| "development".to_string());
let service = DualProviderTradingService::new(&database_url, &environment).await?;
// Initialize providers
service.initialize_providers().await?;
// Start configuration monitoring
service.start_config_monitoring().await?;
// Example: Update a configuration at runtime
service
.update_provider_config(
"databento",
"connection_timeout_ms",
&45000u32,
Some("Increased timeout for better reliability"),
)
.await?;
// Get cache statistics
let (total_entries, expired_entries) = service.get_cache_stats().await;
info!(
"📊 Cache Statistics: {} total, {} expired",
total_entries, expired_entries
);
// Keep the service running
info!("🚀 Dual-provider service running with hot-reload support...");
loop {
sleep(Duration::from_secs(60)).await;
// Periodic health check
let active_providers = service
.config_loader
.get_active_providers(Some(&environment))
.await?;
info!("💓 Health check - Active providers: {:?}", active_providers);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_dual_provider_service_creation() {
// This test would require a database connection
// In practice, you would use a test database
let database_url = "postgresql://localhost/foxhunt_test";
let result = DualProviderTradingService::new(database_url, "test").await;
// This will fail without a database, but demonstrates the API
assert!(result.is_err() || result.is_ok());
}
info!("🚀 Dual-Provider Configuration Integration Example");
info!("⚠️ This is a stub example - provider configuration system needs implementation");
// Example of basic config manager usage
let db_config = DatabaseConfig::default();
info!("Database config: {:?}", db_config);
info!("✅ Example completed");
Ok(())
}