use std::time::Duration;
use async_trait::async_trait;
use hive_router_config::primitives::file_path::FilePath;
use hive_router_internal::telemetry::logging::targets;
use tokio::{fs, sync::RwLock};
use tracing::{debug, trace};
use crate::supergraph::base::{LoadSupergraphError, ReloadSupergraphResult, SupergraphLoader};
#[derive(Debug, thiserror::Error)]
pub enum FileSupergraphError {
#[error("Failed to read supergraph file: {0}")]
ReadFileError(#[from] std::io::Error),
#[error("Supergraph file path is missing. Please provide it via 'SUPERGRAPH_FILE_PATH' environment variable or under 'supergraph.path' in the configuration.")]
MissingSupergraphFilePath,
}
pub struct SupergraphFileLoader {
file_path: FilePath,
poll_interval: Option<Duration>,
modified_time: RwLock<Option<std::time::SystemTime>>,
}
impl SupergraphFileLoader {
async fn load_with_polling(&self) -> Result<ReloadSupergraphResult, FileSupergraphError> {
let file_metadata = fs::metadata(&self.file_path.absolute).await?;
let current_time = file_metadata.modified()?;
let mut modified_time = self.modified_time.write().await;
if modified_time.is_none() || current_time > modified_time.unwrap() {
let content = fs::read_to_string(&self.file_path.absolute).await?;
*modified_time = Some(current_time);
Ok(ReloadSupergraphResult::Changed { new_sdl: content })
} else {
Ok(ReloadSupergraphResult::Unchanged)
}
}
async fn load_without_polling(&self) -> Result<ReloadSupergraphResult, FileSupergraphError> {
let content = fs::read_to_string(&self.file_path.absolute).await?;
Ok(ReloadSupergraphResult::Changed { new_sdl: content })
}
}
#[async_trait]
impl SupergraphLoader for SupergraphFileLoader {
async fn load(&self) -> Result<ReloadSupergraphResult, LoadSupergraphError> {
let result = if self.poll_interval.is_some() {
debug!(
target: targets::SUPERGRAPH,
path = ?self.file_path.absolute,
interval_ms = ?self.poll_interval.as_ref().map(|i| i.as_millis()),
"Loading supergraph from file, and checking metadata for polling",
);
self.load_with_polling().await
} else {
debug!(
target: targets::SUPERGRAPH,
path = ?self.file_path.absolute,
"Loading supergraph from file",
);
self.load_without_polling().await
};
trace!(
target: targets::SUPERGRAPH,
path = ?self.file_path.absolute,
result = ?result,
"Supergraph loaded from file",
);
Ok(result?)
}
fn reload_interval(&self) -> Option<std::time::Duration> {
self.poll_interval
}
}
impl SupergraphFileLoader {
pub fn new(
file_path: &FilePath,
poll_interval: Option<Duration>,
) -> Result<Box<Self>, FileSupergraphError> {
debug!(
target: targets::SUPERGRAPH,
path = ?file_path.absolute,
"Creating supergraph source from file",
);
Ok(Box::new(Self {
file_path: file_path.clone(),
poll_interval,
modified_time: RwLock::new(None),
}))
}
}