use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt;
use crate::utils::MapAccessSerdeExt;
use ntex::util::Bytes;
use serde::de;
use serde::de::IgnoredAny;
use serde::Deserialize;
use serde::Deserializer;
use serde::Serialize;
use sonic_rs::Value;
use crate::executor::plugin_context::PluginContext;
use crate::executor::plugin_context::RouterHttpRequest;
use crate::executor::plugin_trait::EndHookPayload;
use crate::executor::plugin_trait::EndHookResult;
use crate::executor::plugin_trait::StartHookPayload;
use crate::executor::plugin_trait::StartHookResult;
use crate::executor::request_context::RequestContextPluginApi;
use ntex::http::Response;
type RequestContextApi = RequestContextPluginApi<super::OnGraphqlParams>;
#[derive(Debug, Default, Serialize)]
pub struct GraphQLParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub query: Option<String>,
#[serde(rename = "operationName", skip_serializing_if = "Option::is_none")]
pub operation_name: Option<String>,
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub variables: HashMap<String, Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub extensions: Option<HashMap<String, Value>>,
}
impl<'de> Deserialize<'de> for GraphQLParams {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct GraphQLParamsVisitor;
impl<'de> de::Visitor<'de> for GraphQLParamsVisitor {
type Value = GraphQLParams;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a map for GraphQLParams")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: de::MapAccess<'de>,
{
let mut query = None;
let mut operation_name = None;
let mut variables: Option<HashMap<String, Value>> = None;
let mut extensions: Option<HashMap<String, Value>> = None;
while let Some(key) = map.next_key::<Cow<'de, str>>()? {
match key.as_ref() {
"query" => map.deserialize_once_into_option(&mut query, "query")?,
"operationName" => {
map.deserialize_once_into_option(&mut operation_name, "operationName")?
}
"variables" => {
map.deserialize_once_into_option(&mut variables, "variables")?
}
"extensions" => {
map.deserialize_once_into_option(&mut extensions, "extensions")?
}
_ => {
let _ = map.next_value::<IgnoredAny>()?;
}
}
}
Ok(GraphQLParams {
query,
operation_name,
variables: variables.unwrap_or_default(),
extensions,
})
}
}
deserializer.deserialize_map(GraphQLParamsVisitor)
}
}
pub struct OnGraphQLParamsStartHookPayload<'exec> {
pub router_http_request: &'exec RouterHttpRequest<'exec>,
pub context: &'exec PluginContext,
pub request_context: RequestContextApi,
pub body: Bytes,
pub graphql_params: Option<GraphQLParams>,
}
impl<'exec> OnGraphQLParamsStartHookPayload<'exec> {
pub fn with_graphql_params(mut self, graphql_params: GraphQLParams) -> Self {
self.graphql_params = Some(graphql_params);
self
}
}
impl<'exec> StartHookPayload<OnGraphQLParamsEndHookPayload<'exec>, Response>
for OnGraphQLParamsStartHookPayload<'exec>
{
}
pub type OnGraphQLParamsStartHookResult<'exec> = StartHookResult<
'exec,
OnGraphQLParamsStartHookPayload<'exec>,
OnGraphQLParamsEndHookPayload<'exec>,
Response,
>;
pub struct OnGraphQLParamsEndHookPayload<'exec> {
pub graphql_params: GraphQLParams,
pub context: &'exec PluginContext,
pub request_context: RequestContextApi,
}
impl<'exec> EndHookPayload<Response> for OnGraphQLParamsEndHookPayload<'exec> {}
pub type OnGraphQLParamsEndHookResult<'exec> =
EndHookResult<OnGraphQLParamsEndHookPayload<'exec>, Response>;
#[cfg(test)]
use ntex::web::test;
#[cfg(test)]
impl Into<test::TestRequest> for GraphQLParams {
fn into(self) -> test::TestRequest {
let body = self;
test::TestRequest::post().uri("/graphql").set_json(&body)
}
}