use super::model::{AuthType, ServiceConfig};
use crate::circuitbreaker::breaker::{self, CircuitBreaker, CircuitBreakerConfig};
use crate::gateway::gateway::GrpcGateway;
use crate::registry::api_key::APIKeyAuth;
use crate::registry::auth::AuthConfig;
use crate::registry::jwt_token::JWTTokenAuth;
use crate::registry::model::InternalAuthConfig;
use crate::utils::errors::ResponseErrors;
use crate::utils::model::ServiceRegisterRequest;
use crate::utils::validation_errors::ValidationError;
use anyhow::Result;
use serde_json::json;
use std::error::Error;
use std::time::Duration;
use std::{collections::HashMap, sync::Mutex};
use lazy_static::lazy_static;
lazy_static! {
static ref GLOBAL_MAP: Mutex<HashMap<String, ServiceConfig>> = Mutex::new(HashMap::new());
}
pub trait RegistryTrait {
fn validate_oauth_config(
&self,
oauth_config: InternalAuthConfig,
service_endpoint: String,
) -> impl std::future::Future<Output = Result<(), Box<dyn Error>>> + Send;
fn register(
&self,
req: ServiceRegisterRequest,
) -> impl std::future::Future<Output = Result<Option<String>, Box<dyn Error>>> + Send;
fn discover(&self, service_name: String) -> Option<ServiceConfig>;
}
pub struct ServiceRegistry {}
impl RegistryTrait for ServiceRegistry {
async fn register(
&self,
req: ServiceRegisterRequest,
) -> Result<Option<String>, Box<dyn Error>> {
let val = format!("http://{}:{}", req.host, req.port);
let mut config = ServiceConfig {
endpoint: val.to_string(),
service_name: req.service_name.to_string(),
auth_config: None,
breaker: None,
};
let validation_res = self
.validate_oauth_config(req.oauth_config.clone(), val.to_string())
.await;
if validation_res.is_err() {
return Err(Box::new(ValidationError(
validation_res.err().unwrap().to_string(),
)));
}
let auth_config = req.oauth_config.auth_refresh_config.unwrap();
match req.oauth_config.auth_type {
AuthType::APIKey => {
config.auth_config = Some(AuthConfig::APIKeyAuth(APIKeyAuth::new(
auth_config.header_name,
auth_config.access_token,
)))
}
AuthType::JWTToken => {
config.auth_config = Some(AuthConfig::JWTTokenAuth(JWTTokenAuth::new(
auth_config.header_name,
auth_config.access_token,
auth_config.refresh_token,
auth_config.expired_at,
auth_config.service_name,
auth_config.method,
)));
}
};
let breaker = CircuitBreaker::new(CircuitBreakerConfig::default());
config.breaker = Some(breaker);
match GLOBAL_MAP.lock() {
Ok(mut mp) => {
mp.insert(req.service_name.to_string(), config);
Ok(Some(val))
}
Err(e) => Err(Box::new(ValidationError(e.to_string()))),
}
}
fn discover(&self, service_name: String) -> Option<ServiceConfig> {
match GLOBAL_MAP.lock() {
Ok(mp) => mp.get(&service_name).cloned(),
Err(_) => None,
}
}
async fn validate_oauth_config(
&self,
oauth_config: InternalAuthConfig,
service_endpoint: String,
) -> Result<(), Box<dyn Error>> {
match oauth_config.auth_type {
AuthType::APIKey => Ok(()),
AuthType::JWTToken => {
if oauth_config.auth_refresh_config.is_none() {
return Err(Box::new(ValidationError(
ResponseErrors::OAuthRefreshConfigMissingError.to_string(),
)));
};
let refresh_config = oauth_config.auth_refresh_config.unwrap();
if refresh_config.service_name.is_empty() || refresh_config.method.is_empty() {
return Err(Box::new(ValidationError(
ResponseErrors::OAuthRefreshConfigMissingError.to_string(),
)));
};
match GrpcGateway::new(&service_endpoint).await {
Ok(current_gateway) => {
match current_gateway
.refresh_oauth(
&refresh_config.service_name,
&refresh_config.method,
json!({
"refresh_token":refresh_config.refresh_token
}),
)
.await
{
Ok(_) => Ok(()),
Err(_) => Err(Box::new(ValidationError(String::from(
"faild to refresh oauth config",
)))),
}
}
Err(_) => Err(Box::new(ValidationError(
ResponseErrors::ServiceUnAvailable.to_string(),
))),
}
}
}
}
}