refactor(api_gateway): rename ConfigError to GatewayConfigError to avoid collision with config crate
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
//! gRPC endpoints for configuration management
|
||||
|
||||
use crate::config::ConfigurationManager;
|
||||
use crate::error::ConfigError;
|
||||
use crate::error::GatewayConfigError;
|
||||
use tonic::{Request, Response, Status};
|
||||
use tracing::{debug, info};
|
||||
|
||||
@@ -46,7 +46,7 @@ impl ConfigurationService for ConfigurationServiceImpl {
|
||||
.get_config(&req.service_scope, &req.config_key)
|
||||
.await
|
||||
.map_err(|e| match e {
|
||||
ConfigError::NotFound { .. } => Status::not_found(format!("{}", e)),
|
||||
GatewayConfigError::NotFound { .. } => Status::not_found(format!("{}", e)),
|
||||
_ => Status::internal(format!("{}", e)),
|
||||
})?;
|
||||
|
||||
@@ -84,8 +84,8 @@ impl ConfigurationService for ConfigurationServiceImpl {
|
||||
)
|
||||
.await
|
||||
.map_err(|e| match e {
|
||||
ConfigError::Validation(msg) => Status::invalid_argument(msg),
|
||||
ConfigError::NotFound { .. } => Status::not_found(format!("{}", e)),
|
||||
GatewayConfigError::Validation(msg) => Status::invalid_argument(msg),
|
||||
GatewayConfigError::NotFound { .. } => Status::not_found(format!("{}", e)),
|
||||
_ => Status::internal(format!("{}", e)),
|
||||
})?;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Configuration management with PostgreSQL NOTIFY/LISTEN hot-reload and Redis caching
|
||||
|
||||
use crate::config::validator::ConfigValidator;
|
||||
use crate::error::{ConfigError, ConfigResult};
|
||||
use crate::error::{GatewayConfigError, GatewayConfigResult};
|
||||
use chrono::{DateTime, Utc};
|
||||
use redis::aio::ConnectionManager;
|
||||
use serde_json::Value;
|
||||
@@ -46,7 +46,7 @@ impl ConfigurationManager {
|
||||
/// * `db_pool` - Postgre`SQL` connection pool
|
||||
///
|
||||
/// * `redis` - Redis connection manager
|
||||
pub async fn new(db_pool: PgPool, redis: ConnectionManager) -> ConfigResult<Self> {
|
||||
pub async fn new(db_pool: PgPool, redis: ConnectionManager) -> GatewayConfigResult<Self> {
|
||||
Ok(Self {
|
||||
db_pool: Arc::new(db_pool),
|
||||
redis: Arc::new(RwLock::new(redis)),
|
||||
@@ -56,7 +56,7 @@ impl ConfigurationManager {
|
||||
}
|
||||
|
||||
/// Starts listening for configuration changes via Postgre`SQL` NOTIFY
|
||||
pub async fn start_listening(&mut self) -> ConfigResult<()> {
|
||||
pub async fn start_listening(&mut self) -> GatewayConfigResult<()> {
|
||||
let mut listener = sqlx::postgres::PgListener::connect_with(&self.db_pool).await?;
|
||||
|
||||
// Listen to global config updates channel
|
||||
@@ -69,7 +69,7 @@ impl ConfigurationManager {
|
||||
}
|
||||
|
||||
/// Processes configuration change notifications
|
||||
pub async fn handle_notifications(&mut self) -> ConfigResult<()> {
|
||||
pub async fn handle_notifications(&mut self) -> GatewayConfigResult<()> {
|
||||
// Collect notifications first to avoid borrow checker issues
|
||||
let mut invalidations = Vec::new();
|
||||
|
||||
@@ -113,7 +113,7 @@ impl ConfigurationManager {
|
||||
&self,
|
||||
service_scope: &str,
|
||||
config_key: &str,
|
||||
) -> ConfigResult<ConfigItem> {
|
||||
) -> GatewayConfigResult<ConfigItem> {
|
||||
// Try Redis cache first
|
||||
if let Some(cached) = self.get_from_cache(service_scope, config_key).await? {
|
||||
debug!("Cache hit for {}/{}", service_scope, config_key);
|
||||
@@ -145,7 +145,7 @@ impl ConfigurationManager {
|
||||
config_key: &str,
|
||||
new_value: Value,
|
||||
updated_by: &str,
|
||||
) -> ConfigResult<()> {
|
||||
) -> GatewayConfigResult<()> {
|
||||
// Load current configuration for validation rules and audit
|
||||
let current = self.load_from_db(service_scope, config_key).await?;
|
||||
|
||||
@@ -207,7 +207,7 @@ impl ConfigurationManager {
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `service_scope` - Service scope (None for all scopes)
|
||||
pub async fn list_configs(&self, service_scope: Option<&str>) -> ConfigResult<Vec<ConfigItem>> {
|
||||
pub async fn list_configs(&self, service_scope: Option<&str>) -> GatewayConfigResult<Vec<ConfigItem>> {
|
||||
let configs = if let Some(scope) = service_scope {
|
||||
sqlx::query_as::<_, ConfigItem>(
|
||||
r#"
|
||||
@@ -239,7 +239,7 @@ impl ConfigurationManager {
|
||||
&self,
|
||||
service_scope: &str,
|
||||
config_key: &str,
|
||||
) -> ConfigResult<ConfigItem> {
|
||||
) -> GatewayConfigResult<ConfigItem> {
|
||||
let config = sqlx::query_as::<_, ConfigItem>(
|
||||
r#"
|
||||
SELECT * FROM config_settings
|
||||
@@ -250,7 +250,7 @@ impl ConfigurationManager {
|
||||
.bind(config_key)
|
||||
.fetch_optional(&*self.db_pool)
|
||||
.await?
|
||||
.ok_or_else(|| ConfigError::NotFound {
|
||||
.ok_or_else(|| GatewayConfigError::NotFound {
|
||||
service_scope: service_scope.to_string(),
|
||||
key: config_key.to_string(),
|
||||
})?;
|
||||
@@ -263,7 +263,7 @@ impl ConfigurationManager {
|
||||
&self,
|
||||
service_scope: &str,
|
||||
config_key: &str,
|
||||
) -> ConfigResult<Option<ConfigItem>> {
|
||||
) -> GatewayConfigResult<Option<ConfigItem>> {
|
||||
let redis_key = format!("config:{}:{}", service_scope, config_key);
|
||||
let mut redis = self.redis.write().await;
|
||||
|
||||
@@ -271,7 +271,7 @@ impl ConfigurationManager {
|
||||
.arg(&redis_key)
|
||||
.query_async(&mut *redis)
|
||||
.await
|
||||
.map_err(ConfigError::Redis)?;
|
||||
.map_err(GatewayConfigError::Redis)?;
|
||||
|
||||
if let Some(cached_json) = cached {
|
||||
let config: ConfigItem = serde_json::from_str(&cached_json)?;
|
||||
@@ -282,7 +282,7 @@ impl ConfigurationManager {
|
||||
}
|
||||
|
||||
/// Stores configuration in Redis cache
|
||||
async fn set_in_cache(&self, config: &ConfigItem) -> ConfigResult<()> {
|
||||
async fn set_in_cache(&self, config: &ConfigItem) -> GatewayConfigResult<()> {
|
||||
let redis_key = format!("config:{}:{}", config.service_scope, config.config_key);
|
||||
let config_json = serde_json::to_string(config)?;
|
||||
let mut redis = self.redis.write().await;
|
||||
@@ -294,13 +294,13 @@ impl ConfigurationManager {
|
||||
.arg(&config_json)
|
||||
.query_async::<()>(&mut *redis)
|
||||
.await
|
||||
.map_err(ConfigError::Redis)?;
|
||||
.map_err(GatewayConfigError::Redis)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Invalidates Redis cache for a configuration
|
||||
async fn invalidate_cache(&self, service_scope: &str, config_key: &str) -> ConfigResult<()> {
|
||||
async fn invalidate_cache(&self, service_scope: &str, config_key: &str) -> GatewayConfigResult<()> {
|
||||
let redis_key = format!("config:{}:{}", service_scope, config_key);
|
||||
let mut redis = self.redis.write().await;
|
||||
|
||||
@@ -308,7 +308,7 @@ impl ConfigurationManager {
|
||||
.arg(&redis_key)
|
||||
.query_async::<()>(&mut *redis)
|
||||
.await
|
||||
.map_err(ConfigError::Redis)?;
|
||||
.map_err(GatewayConfigError::Redis)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Configuration validation with type checking, range validation, and regex matching
|
||||
|
||||
use crate::error::{ConfigError, ConfigResult};
|
||||
use crate::error::{GatewayConfigError, GatewayConfigResult};
|
||||
use regex::Regex;
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
@@ -53,7 +53,7 @@ impl ConfigValidator {
|
||||
value: &Value,
|
||||
data_type: &str,
|
||||
rules: Option<&Value>,
|
||||
) -> ConfigResult<()> {
|
||||
) -> GatewayConfigResult<()> {
|
||||
// Parse validation rules if provided
|
||||
let validation_rules: Option<ValidationRules> = match rules {
|
||||
Some(r) => serde_json::from_value(r.clone()).ok(),
|
||||
@@ -77,7 +77,7 @@ impl ConfigValidator {
|
||||
}
|
||||
|
||||
/// Validates that the value matches the expected type
|
||||
fn validate_type(&self, value: &Value, data_type: &str) -> ConfigResult<()> {
|
||||
fn validate_type(&self, value: &Value, data_type: &str) -> GatewayConfigResult<()> {
|
||||
let matches = match data_type {
|
||||
"string" => value.is_string(),
|
||||
"integer" => value.is_i64() || value.is_u64(),
|
||||
@@ -86,7 +86,7 @@ impl ConfigValidator {
|
||||
"json" => value.is_object(),
|
||||
"array" => value.is_array(),
|
||||
_ => {
|
||||
return Err(ConfigError::Validation(format!(
|
||||
return Err(GatewayConfigError::Validation(format!(
|
||||
"Unknown data type: {}",
|
||||
data_type
|
||||
)))
|
||||
@@ -94,7 +94,7 @@ impl ConfigValidator {
|
||||
};
|
||||
|
||||
if !matches {
|
||||
return Err(ConfigError::Validation(format!(
|
||||
return Err(GatewayConfigError::Validation(format!(
|
||||
"Type mismatch: expected {}, got {}",
|
||||
data_type,
|
||||
value_type_name(value)
|
||||
@@ -105,14 +105,14 @@ impl ConfigValidator {
|
||||
}
|
||||
|
||||
/// Validates numeric values against min/max rules
|
||||
fn validate_numeric(&self, value: &Value, rules: &ValidationRules) -> ConfigResult<()> {
|
||||
fn validate_numeric(&self, value: &Value, rules: &ValidationRules) -> GatewayConfigResult<()> {
|
||||
let num = value
|
||||
.as_f64()
|
||||
.ok_or_else(|| ConfigError::Validation("Not a number".to_string()))?;
|
||||
.ok_or_else(|| GatewayConfigError::Validation("Not a number".to_string()))?;
|
||||
|
||||
if let Some(min) = rules.min {
|
||||
if num < min {
|
||||
return Err(ConfigError::Validation(format!(
|
||||
return Err(GatewayConfigError::Validation(format!(
|
||||
"Value {} is below minimum {}",
|
||||
num, min
|
||||
)));
|
||||
@@ -121,7 +121,7 @@ impl ConfigValidator {
|
||||
|
||||
if let Some(max) = rules.max {
|
||||
if num > max {
|
||||
return Err(ConfigError::Validation(format!(
|
||||
return Err(GatewayConfigError::Validation(format!(
|
||||
"Value {} exceeds maximum {}",
|
||||
num, max
|
||||
)));
|
||||
@@ -132,15 +132,15 @@ impl ConfigValidator {
|
||||
}
|
||||
|
||||
/// Validates string values against length and regex rules
|
||||
fn validate_string(&mut self, value: &Value, rules: &ValidationRules) -> ConfigResult<()> {
|
||||
fn validate_string(&mut self, value: &Value, rules: &ValidationRules) -> GatewayConfigResult<()> {
|
||||
let s = value
|
||||
.as_str()
|
||||
.ok_or_else(|| ConfigError::Validation("Not a string".to_string()))?;
|
||||
.ok_or_else(|| GatewayConfigError::Validation("Not a string".to_string()))?;
|
||||
|
||||
// Length validation
|
||||
if let Some(min_len) = rules.min_len {
|
||||
if s.len() < min_len {
|
||||
return Err(ConfigError::Validation(format!(
|
||||
return Err(GatewayConfigError::Validation(format!(
|
||||
"String length {} is below minimum {}",
|
||||
s.len(),
|
||||
min_len
|
||||
@@ -150,7 +150,7 @@ impl ConfigValidator {
|
||||
|
||||
if let Some(max_len) = rules.max_len {
|
||||
if s.len() > max_len {
|
||||
return Err(ConfigError::Validation(format!(
|
||||
return Err(GatewayConfigError::Validation(format!(
|
||||
"String length {} exceeds maximum {}",
|
||||
s.len(),
|
||||
max_len
|
||||
@@ -162,17 +162,17 @@ impl ConfigValidator {
|
||||
if let Some(pattern) = &rules.regex {
|
||||
if !self.regex_cache.contains_key(pattern) {
|
||||
let compiled = Regex::new(pattern).map_err(|e| {
|
||||
ConfigError::Validation(format!("Invalid regex pattern '{}': {}", pattern, e))
|
||||
GatewayConfigError::Validation(format!("Invalid regex pattern '{}': {}", pattern, e))
|
||||
})?;
|
||||
self.regex_cache.insert(pattern.clone(), compiled);
|
||||
}
|
||||
let regex = self
|
||||
.regex_cache
|
||||
.get(pattern)
|
||||
.ok_or_else(|| ConfigError::Validation("Regex cache miss".to_owned()))?;
|
||||
.ok_or_else(|| GatewayConfigError::Validation("Regex cache miss".to_owned()))?;
|
||||
|
||||
if !regex.is_match(s) {
|
||||
return Err(ConfigError::Validation(format!(
|
||||
return Err(GatewayConfigError::Validation(format!(
|
||||
"String '{}' does not match pattern '{}'",
|
||||
s, pattern
|
||||
)));
|
||||
@@ -182,7 +182,7 @@ impl ConfigValidator {
|
||||
// Enum validation
|
||||
if let Some(enum_values) = &rules.enum_values {
|
||||
if !enum_values.contains(&s.to_string()) {
|
||||
return Err(ConfigError::Validation(format!(
|
||||
return Err(GatewayConfigError::Validation(format!(
|
||||
"Value '{}' is not in allowed values: {:?}",
|
||||
s, enum_values
|
||||
)));
|
||||
@@ -193,14 +193,14 @@ impl ConfigValidator {
|
||||
}
|
||||
|
||||
/// Validates array values against length rules
|
||||
fn validate_array(&self, value: &Value, rules: &ValidationRules) -> ConfigResult<()> {
|
||||
fn validate_array(&self, value: &Value, rules: &ValidationRules) -> GatewayConfigResult<()> {
|
||||
let arr = value
|
||||
.as_array()
|
||||
.ok_or_else(|| ConfigError::Validation("Not an array".to_string()))?;
|
||||
.ok_or_else(|| GatewayConfigError::Validation("Not an array".to_string()))?;
|
||||
|
||||
if let Some(min_len) = rules.min_len {
|
||||
if arr.len() < min_len {
|
||||
return Err(ConfigError::Validation(format!(
|
||||
return Err(GatewayConfigError::Validation(format!(
|
||||
"Array length {} is below minimum {}",
|
||||
arr.len(),
|
||||
min_len
|
||||
@@ -210,7 +210,7 @@ impl ConfigValidator {
|
||||
|
||||
if let Some(max_len) = rules.max_len {
|
||||
if arr.len() > max_len {
|
||||
return Err(ConfigError::Validation(format!(
|
||||
return Err(GatewayConfigError::Validation(format!(
|
||||
"Array length {} exceeds maximum {}",
|
||||
arr.len(),
|
||||
max_len
|
||||
|
||||
@@ -4,7 +4,7 @@ use thiserror::Error;
|
||||
|
||||
/// Configuration management errors
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ConfigError {
|
||||
pub enum GatewayConfigError {
|
||||
#[error("Database error: {0}")]
|
||||
Database(#[from] sqlx::Error),
|
||||
|
||||
@@ -28,4 +28,4 @@ pub enum ConfigError {
|
||||
}
|
||||
|
||||
/// Result type for configuration operations
|
||||
pub type ConfigResult<T> = Result<T, ConfigError>;
|
||||
pub type GatewayConfigResult<T> = Result<T, GatewayConfigError>;
|
||||
|
||||
@@ -64,7 +64,7 @@ pub mod metrics;
|
||||
pub mod routing;
|
||||
|
||||
// Re-export error types
|
||||
pub use error::{ConfigError, ConfigResult};
|
||||
pub use error::{GatewayConfigError, GatewayConfigResult};
|
||||
|
||||
// Re-export configuration management types
|
||||
pub use config::{
|
||||
|
||||
Reference in New Issue
Block a user