hive_router_plan_executor/plugins/hooks/on_graphql_validation.rs
1use std::sync::Arc;
2
3use graphql_tools::{
4 static_graphql::{query::Document as QueryDocument, schema::Document as SchemaDocument},
5 validation::{rules::ValidationRule, utils::ValidationError, validate::ValidationPlan},
6};
7use hive_router_query_planner::consumer_schema::ConsumerSchema;
8use ntex::http::Response;
9
10use crate::{
11 plugin_context::{PluginContext, RouterHttpRequest},
12 plugin_trait::{CacheHint, EndHookPayload, EndHookResult, StartHookPayload, StartHookResult},
13};
14
15pub struct OnGraphQLValidationStartHookPayload<'exec> {
16 /// The incoming HTTP request to the router for which the GraphQL execution is happening.
17 /// It includes all the details of the request such as headers, body, etc.
18 ///
19 /// Example:
20 /// ```
21 /// let my_header = payload.router_http_request.headers.get("my-header");
22 /// // do something with the header...
23 /// payload.proceed()
24 /// ```
25 pub router_http_request: &'exec RouterHttpRequest<'exec>,
26 /// The context object that can be used to share data across different plugin hooks for the same request.
27 /// It is unique per request and is dropped after the response is sent.
28 ///
29 /// [Learn more about the context data sharing in the docs](https://the-guild.dev/graphql/hive/docs/router/extensibility/plugin_system#context-data-sharing)
30 pub context: &'exec PluginContext,
31 /// The GraphQL Schema that the document will be validated against.
32 /// This is not the same with the supergraph. This is the public schema exposed by the router to the clients, which is generated from the supergraph and can be modified by the plugins.
33 /// The plugins can replace the input schema to be used for validation
34 /// and the new schema will be used in the validation process instead of the original one.
35 ///
36 /// [See an example to see when to override the schema](https://github.com/graphql-hive/router/blob/main/plugin_examples/feature_flags/src/plugin.rs)
37 pub schema: Arc<ConsumerSchema>,
38 /// Parsed GraphQL document from the query string in the GraphQL parameters.
39 /// It contains the Abstract Syntax Tree (AST) representation of the GraphQL query, mutation, or subscription
40 /// sent by the client in the request body.
41 ///
42 /// But the plugins can replace the input document AST to be used for validation
43 /// and the new document will be used in the validation process instead of the original one.
44 pub document: Arc<QueryDocument>,
45 /// The set of rules to be used in the validation process.
46 /// The plugins can modify the validation rules to be used in the validation process by adding new rules or
47 /// removing existing ones.
48 ///
49 /// [See an example](https://github.com/graphql-hive/router/blob/main/plugin_examples/root_field_limit/src/lib.rs#:~:text=fn%20on_graphql_validation)
50 pub validation_plan: Arc<ValidationPlan>,
51 /// Overriding the validation errors to be used in the execution instead of the ones generated from the validation process.
52 /// This is useful for plugins that want to generate custom validation errors in a custom way,
53 /// or want to override the validation errors for testing or other purposes.
54 ///
55 /// [Learn more about overriding the default behavior](https://the-guild.dev/graphql/hive/docs/router/extensibility/plugin_system#overriding-default-behavior)
56 pub errors: Option<Arc<Vec<ValidationError>>>,
57}
58
59impl OnGraphQLValidationStartHookPayload<'_> {
60 /// Override validation rules to be used in the validation process by adding a new rule.
61 /// [See an example](https://github.com/graphql-hive/router/blob/main/plugin_examples/root_field_limit/src/lib.rs#:~:text=fn%20on_graphql_validation)
62 pub fn with_validation_plan<TValidationPlan: Into<ValidationPlan>>(
63 mut self,
64 validation_plan: TValidationPlan,
65 ) -> Self {
66 self.validation_plan = Arc::new(validation_plan.into());
67 self
68 }
69 /// Override the GraphQL Schema that the document will be validated against.
70 /// [See an example to see when to override the schema](https://github.com/graphql-hive/router/blob/main/plugin_examples/feature_flags/src/plugin.rs)
71 pub fn with_schema<TSchema: Into<Arc<SchemaDocument>>>(mut self, schema: TSchema) -> Self {
72 let schema: Arc<SchemaDocument> = schema.into();
73 let new_consumer_schema = ConsumerSchema::from(schema);
74 self.schema = new_consumer_schema.into();
75 self
76 }
77}
78
79impl<'exec> StartHookPayload<OnGraphQLValidationEndHookPayload, Response>
80 for OnGraphQLValidationStartHookPayload<'exec>
81{
82}
83
84pub type OnGraphQLValidationStartHookResult<'exec> = StartHookResult<
85 'exec,
86 OnGraphQLValidationStartHookPayload<'exec>,
87 OnGraphQLValidationEndHookPayload,
88 Response,
89>;
90
91impl<'exec> OnGraphQLValidationStartHookPayload<'exec> {
92 /// Adds a new validation rule to the existing set of rules to be used in the validation process.
93 /// [See an example](https://github.com/graphql-hive/router/blob/main/plugin_examples/root_field_limit/src/lib.rs#:~:text=fn%20on_graphql_validation)
94 pub fn with_validation_rule<TValidationRule: ValidationRule + 'static>(
95 mut self,
96 rule: TValidationRule,
97 ) -> Self {
98 let mut new_plan = self.validation_plan.as_ref().clone();
99 new_plan.add_rule(Box::new(rule));
100 self.validation_plan = Arc::new(new_plan);
101 self
102 }
103
104 /// Filters the existing validation rules to be used in the validation process by removing the rules that don't satisfy the given predicate function.
105 pub fn filter_validation_rules<F>(mut self, mut f: F) -> Self
106 where
107 F: FnMut(&Box<dyn ValidationRule>) -> bool,
108 {
109 let mut new_plan = self.validation_plan.as_ref().clone();
110 new_plan.rules.retain(|rule| f(rule));
111 self.validation_plan = Arc::new(new_plan);
112 self
113 }
114}
115
116pub struct OnGraphQLValidationEndHookPayload {
117 pub errors: Arc<Vec<ValidationError>>,
118 pub cache_hint: CacheHint,
119}
120
121impl EndHookPayload<Response> for OnGraphQLValidationEndHookPayload {}
122
123pub type OnGraphQLValidationHookEndResult =
124 EndHookResult<OnGraphQLValidationEndHookPayload, Response>;