//! Extensions for schema mod analyzer; #[cfg(feature = "apollo_persisted_queries")] pub mod apollo_persisted_queries; #[cfg(feature = "apollo_tracing")] mod apollo_tracing; #[cfg(feature = "log")] mod logger; #[cfg(feature = "opentelemetry")] mod opentelemetry; #[cfg(feature = "tracing")] mod tracing; use std::any::{Any, TypeId}; use std::collections::BTreeMap; use crate::context::{QueryPathNode, ResolveId}; use crate::parser::types::ExecutableDocument; use crate::{Data, Request, Result, ServerError, ServerResult, ValidationResult, Variables}; use crate::{Error, Name, Value}; pub use self::analyzer::Analyzer; #[cfg(feature = "apollo_tracing")] pub use self::apollo_tracing::ApolloTracing; #[cfg(feature = "log")] pub use self::logger::Logger; #[cfg(feature = "opentelemetry")] pub use self::opentelemetry::OpenTelemetry; #[cfg(feature = "tracing")] pub use self::tracing::{Tracing, TracingConfig}; pub(crate) type BoxExtension = Box; /// Context for extension pub struct ExtensionContext<'a> { #[doc(hidden)] pub schema_data: &'a Data, #[doc(hidden)] pub query_data: &'a Data, } impl<'a> ExtensionContext<'a> { /// Gets the global data defined in the `Context` or `Schema`. /// /// If both `Schema` and `Query` have the same data type, the data in the `Query` is obtained. /// /// # Errors /// /// Returns a `Error` if the specified type data does not exist. pub fn data(&self) -> Result<&'a D> { self.data_opt::().ok_or_else(|| { Error::new(format!( "Data `{}` does not exist.", std::any::type_name::() )) }) } /// Gets the global data defined in the `Context` or `Schema`. /// /// # Panics /// /// It will panic if the specified data type does not exist. pub fn data_unchecked(&self) -> &'a D { self.data_opt::() .unwrap_or_else(|| panic!("Data `{}` does not exist.", std::any::type_name::())) } /// Gets the global data defined in the `Context` or `Schema` or `None` if the specified type data does not exist. pub fn data_opt(&self) -> Option<&'a D> { self.query_data .get(&TypeId::of::()) .or_else(|| self.schema_data.get(&TypeId::of::())) .and_then(|d| d.downcast_ref::()) } } /// Parameters for `Extension::resolve_field_start` pub struct ResolveInfo<'a> { /// Because resolver is concurrent, `Extension::resolve_field_start` and `Extension::resolve_field_end` are /// not strictly ordered, so each pair is identified by an id. pub resolve_id: ResolveId, /// Current path node, You can go through the entire path. pub path_node: &'a QueryPathNode<'a>, /// Parent type pub parent_type: &'a str, /// Current return type, is qualified name. pub return_type: &'a str, } /// Represents a GraphQL extension #[async_trait::async_trait] #[allow(unused_variables)] pub trait Extension: Sync + Send + 'static { /// If this extension needs to output data to query results, you need to specify a name. fn name(&self) -> Option<&'static str> { None } /// Called at the prepare request async fn prepare_request( &mut self, ctx: &ExtensionContext<'_>, request: Request, ) -> ServerResult { Ok(request) } /// Called at the begin of the parse. fn parse_start( &mut self, ctx: &ExtensionContext<'_>, query_source: &str, variables: &Variables, ) { } /// Called at the end of the parse. fn parse_end(&mut self, ctx: &ExtensionContext<'_>, document: &ExecutableDocument) {} /// Called at the begin of the validation. fn validation_start(&mut self, ctx: &ExtensionContext<'_>) {} /// Called at the end of the validation. fn validation_end(&mut self, ctx: &ExtensionContext<'_>, result: &ValidationResult) {} /// Called at the begin of the execution. fn execution_start(&mut self, ctx: &ExtensionContext<'_>) {} /// Called at the end of the execution. fn execution_end(&mut self, ctx: &ExtensionContext<'_>) {} /// Called at the begin of the resolve field. fn resolve_start(&mut self, ctx: &ExtensionContext<'_>, info: &ResolveInfo<'_>) {} /// Called at the end of the resolve field. fn resolve_end(&mut self, ctx: &ExtensionContext<'_>, info: &ResolveInfo<'_>) {} /// Called when an error occurs. fn error(&mut self, ctx: &ExtensionContext<'_>, err: &ServerError) {} /// Get the results fn result(&mut self, ctx: &ExtensionContext<'_>) -> Option { None } } pub(crate) trait ErrorLogger { fn log_error(self, ctx: &ExtensionContext<'_>, extensions: &Extensions) -> Self; } impl ErrorLogger for ServerResult { fn log_error(self, ctx: &ExtensionContext<'_>, extensions: &Extensions) -> Self { if let Err(err) = &self { extensions.error(ctx, err); } self } } impl ErrorLogger for Result> { fn log_error(self, ctx: &ExtensionContext<'_>, extensions: &Extensions) -> Self { if let Err(errors) = &self { for error in errors { extensions.error(ctx, error); } } self } } /// Extension factory /// /// Used to create an extension instance. pub trait ExtensionFactory: Send + Sync + 'static { /// Create an extended instance. fn create(&self) -> Box; } #[doc(hidden)] pub struct Extensions(Option>>); impl From> for Extensions { fn from(extensions: Vec) -> Self { Self(if extensions.is_empty() { None } else { Some(spin::Mutex::new(extensions)) }) } } #[doc(hidden)] impl Extensions { pub fn is_empty(&self) -> bool { self.0.is_none() } pub async fn prepare_request( &mut self, ctx: &ExtensionContext<'_>, request: Request, ) -> ServerResult { let mut request = request; if let Some(e) = &mut self.0 { for e in e.get_mut().iter_mut() { request = e.prepare_request(ctx, request).await?; } } Ok(request) } pub fn parse_start( &mut self, ctx: &ExtensionContext<'_>, query_source: &str, variables: &Variables, ) { if let Some(e) = &mut self.0 { e.get_mut() .iter_mut() .for_each(|e| e.parse_start(ctx, query_source, variables)); } } pub fn parse_end(&mut self, ctx: &ExtensionContext<'_>, document: &ExecutableDocument) { if let Some(e) = &mut self.0 { e.get_mut() .iter_mut() .for_each(|e| e.parse_end(ctx, document)); } } pub fn validation_start(&mut self, ctx: &ExtensionContext<'_>) { if let Some(e) = &mut self.0 { e.get_mut().iter_mut().for_each(|e| e.validation_start(ctx)); } } pub fn validation_end(&mut self, ctx: &ExtensionContext<'_>, result: &ValidationResult) { if let Some(e) = &mut self.0 { e.get_mut() .iter_mut() .for_each(|e| e.validation_end(ctx, result)); } } pub fn execution_start(&self, ctx: &ExtensionContext<'_>) { if let Some(e) = &self.0 { e.lock().iter_mut().for_each(|e| e.execution_start(ctx)); } } pub fn execution_end(&self, ctx: &ExtensionContext<'_>) { if let Some(e) = &self.0 { e.lock().iter_mut().for_each(|e| e.execution_end(ctx)); } } pub fn resolve_start(&self, ctx: &ExtensionContext<'_>, info: &ResolveInfo<'_>) { if let Some(e) = &self.0 { e.lock().iter_mut().for_each(|e| e.resolve_start(ctx, info)); } } pub fn resolve_end(&self, ctx: &ExtensionContext<'_>, resolve_id: &ResolveInfo<'_>) { if let Some(e) = &self.0 { e.lock() .iter_mut() .for_each(|e| e.resolve_end(ctx, resolve_id)); } } pub fn error(&self, ctx: &ExtensionContext<'_>, err: &ServerError) { if let Some(e) = &self.0 { e.lock().iter_mut().for_each(|e| e.error(ctx, err)); } } pub fn result(&self, ctx: &ExtensionContext<'_>) -> Option { if let Some(e) = &self.0 { let value = e .lock() .iter_mut() .filter_map(|e| { if let Some(name) = e.name() { e.result(ctx).map(|res| (Name::new(name), res)) } else { None } }) .collect::>(); if value.is_empty() { None } else { Some(Value::Object(value)) } } else { None } } }