use std::path::PathBuf;
use std::sync::Arc;
use multimap::MultiMap;
use schemars::JsonSchema;
use serde::Deserialize;
use serde::Serialize;
use tower::BoxError;
use crate::Endpoint;
use crate::configuration::ListenAddr;
use crate::plugin::Plugin;
use crate::plugin::PluginInit;
mod archive_utils;
mod constants;
mod export;
mod html_generator;
mod memory;
mod response_builder;
mod security;
mod service;
mod static_resources;
pub(crate) mod system_info;
#[cfg(test)]
mod tests;
#[derive(Debug, thiserror::Error)]
pub(crate) enum DiagnosticsError {
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("HTTP error: {0}")]
Http(#[from] http::Error),
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
#[error("Memory profiling error: {0}")]
#[cfg_attr(
not(all(target_family = "unix", feature = "global-allocator")),
allow(dead_code)
)]
Memory(String),
#[error("Internal error: {0}")]
Internal(String),
}
pub(crate) type DiagnosticsResult<T> = Result<T, DiagnosticsError>;
impl From<String> for DiagnosticsError {
fn from(error: String) -> Self {
DiagnosticsError::Internal(error)
}
}
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
#[serde(deny_unknown_fields)]
#[serde(default)]
pub(crate) struct Config {
pub(crate) enabled: bool,
pub(crate) listen: ListenAddr,
pub(crate) output_directory: PathBuf,
}
impl Default for Config {
fn default() -> Self {
Self {
enabled: false,
listen: constants::network::default_listen_addr().into(),
output_directory: PathBuf::from("/tmp/router-diagnostics"),
}
}
}
#[derive(Debug, Clone)]
struct DiagnosticsPlugin {
config: Config,
router_config: Arc<str>,
supergraph_schema: Arc<String>,
}
#[async_trait::async_trait]
impl Plugin for DiagnosticsPlugin {
type Config = Config;
async fn new(init: PluginInit<Self::Config>) -> Result<Self, BoxError> {
Ok(Self {
config: init.config,
supergraph_schema: init.supergraph_sdl,
router_config: init.raw_yaml.unwrap_or(Arc::from("")),
})
}
fn web_endpoints(&self) -> MultiMap<ListenAddr, Endpoint> {
if !self.config.enabled {
return MultiMap::new();
}
let router = service::create_router(
&self.config.output_directory,
self.router_config.clone(),
self.supergraph_schema.clone(),
);
let mut map = MultiMap::new();
let endpoint = Endpoint::from_router("/diagnostics".to_string(), router);
tracing::info!(
"Diagnostics endpoints at {}/diagnostics",
self.config.listen
);
map.insert(self.config.listen.clone(), endpoint);
map
}
}
register_plugin!("apollo", "experimental_diagnostics", DiagnosticsPlugin);