use std::sync::Arc;
use crate::query_planner::ast::operation::OperationDefinition;
use crate::query_planner::state::supergraph_state::OperationKind;
use crate::executor::execution::client_request_details::ClientRequestDetails;
use crate::executor::execution::plan::{CoerceVariablesPayload, PlanExecutionOutput};
use crate::executor::hooks::on_graphql_params::GraphQLParams;
use crate::executor::introspection::schema::SchemaMetadata;
use crate::executor::operation_filter::{OperationFilter, OperationFilterOutput};
use crate::executor::plugin_context::{PluginContext, RouterHttpRequest};
use crate::executor::request_context::RequestContextPluginApi;
type RequestContextApi = RequestContextPluginApi<super::OnGraphqlAnalysis>;
pub use crate::executor::operation_filter::{
FieldInfo, FilterDecision, OperationFilterError, Selection, TypeConditionInfo,
};
type OperationFilterVisitor<'exec> =
Box<dyn FnMut(&Selection<'exec>) -> FilterDecision + Send + 'exec>;
pub struct OnGraphqlAnalysisHookPayload<'exec> {
pub router_http_request: &'exec RouterHttpRequest<'exec>,
pub context: &'exec PluginContext,
pub request_context: RequestContextApi,
pub filtered_operation_for_plan: &'exec OperationDefinition,
pub client_request_details: Arc<ClientRequestDetails<'exec>>,
pub graphql_params: &'exec GraphQLParams,
schema_metadata: &'exec SchemaMetadata,
variable_payload: &'exec CoerceVariablesPayload,
operation_filter_visitors: Vec<OperationFilterVisitor<'exec>>,
}
impl<'exec> OnGraphqlAnalysisHookPayload<'exec> {
#[allow(clippy::too_many_arguments)]
pub fn new(
router_http_request: &'exec RouterHttpRequest<'exec>,
context: &'exec PluginContext,
request_context: RequestContextApi,
filtered_operation_for_plan: &'exec OperationDefinition,
client_request_details: Arc<ClientRequestDetails<'exec>>,
graphql_params: &'exec GraphQLParams,
schema_metadata: &'exec SchemaMetadata,
variable_payload: &'exec CoerceVariablesPayload,
) -> Self {
Self {
router_http_request,
context,
request_context,
filtered_operation_for_plan,
client_request_details,
graphql_params,
schema_metadata,
variable_payload,
operation_filter_visitors: Vec::new(),
}
}
pub fn filter_operation(
&mut self,
visitor: impl FnMut(&Selection<'exec>) -> FilterDecision + Send + 'exec,
) {
self.operation_filter_visitors.push(Box::new(visitor));
}
pub fn run_operation_filters(
self,
) -> Result<OperationFilterOutput<'exec>, OperationFilterError> {
if self.operation_filter_visitors.is_empty() {
return Ok(OperationFilterOutput::default());
}
let schema_metadata = self.schema_metadata;
let operation = self.filtered_operation_for_plan;
let root_type_name = match operation.operation_kind {
None | Some(OperationKind::Query) => schema_metadata.query_type_name.as_deref(),
Some(OperationKind::Mutation) => schema_metadata.mutation_type_name.as_deref(),
Some(OperationKind::Subscription) => schema_metadata.subscription_type_name.as_deref(),
}
.expect("root type name not found in schema metadata");
let mut visitors = self.operation_filter_visitors;
OperationFilter::new(schema_metadata).filter(
root_type_name,
&operation.selection_set,
self.variable_payload,
|selection| {
for visitor in visitors.iter_mut() {
if let FilterDecision::Reject { error } = visitor(selection) {
return FilterDecision::Reject { error };
}
}
FilterDecision::Keep
},
)
}
}
pub enum OnGraphqlAnalysisHookResult {
Proceed,
EndWithResponse(PlanExecutionOutput),
}