use std::{future::Future, pin::Pin};
#[cfg(feature = "router-graphql")]
use async_graphql::{
http::GraphiQLSource, parser::parse_query, Error as GraphQLError, Request as GraphQLRequest,
};
use super::ProtocolAdapter;
#[cfg(feature = "router-graphql")]
pub struct GraphQLProductionAdapter {
playground_endpoint: String,
}
#[cfg(feature = "router-graphql")]
impl GraphQLProductionAdapter {
pub fn new(playground_endpoint: impl Into<String>) -> Self {
Self {
playground_endpoint: playground_endpoint.into(),
}
}
pub fn parse_query(query: &str) -> Result<(), GraphQLError> {
parse_query(query).map(|_| ()).map_err(|e| e.into())
}
pub fn graphiql_source(&self) -> String {
GraphiQLSource::build()
.endpoint(&self.playground_endpoint)
.finish()
}
pub fn validate_request(&self, request: &GraphQLRequest) -> Result<(), String> {
Self::parse_query(request.query.as_str())
.map_err(|e| format!("Invalid GraphQL query: {:?}", e))
}
}
#[cfg(feature = "router-graphql")]
impl ProtocolAdapter for GraphQLProductionAdapter {
fn name(&self) -> &str {
"graphql-production"
}
fn handle(
&self,
request: &str,
) -> Pin<Box<dyn Future<Output = Result<String, String>> + Send + '_>> {
let request = request.to_string();
Box::pin(async move {
let _graphql_request = GraphQLRequest::new(&request);
match Self::parse_query(&request) {
Ok(_) => {
Ok(
r#"{"data":{"message":"Query parsed and validated successfully"}}"#
.to_string(),
)
}
Err(e) => Err(format!("GraphQL parsing error: {:?}", e)),
}
})
}
}
#[cfg(test)]
#[cfg(feature = "router-graphql")]
mod tests {
use super::*;
#[test]
fn test_graphql_query_parsing() {
let valid_query = "{ user(id: 42) { name email } }";
assert!(GraphQLProductionAdapter::parse_query(valid_query).is_ok());
let invalid_query = "{ user(id: ) }";
assert!(GraphQLProductionAdapter::parse_query(invalid_query).is_err());
}
#[test]
fn test_graphql_mutation_parsing() {
let mutation = r#"
mutation CreateUser($name: String!) {
createUser(name: $name) {
id
name
}
}
"#;
assert!(GraphQLProductionAdapter::parse_query(mutation).is_ok());
}
#[test]
fn test_graphql_subscription_parsing() {
let subscription = r#"
subscription OnUserCreated {
userCreated {
id
name
}
}
"#;
assert!(GraphQLProductionAdapter::parse_query(subscription).is_ok());
}
#[tokio::test]
async fn test_adapter_validation() {
let adapter = GraphQLProductionAdapter::new("/graphql");
let valid_request = GraphQLRequest::new("{ hello }");
assert!(adapter.validate_request(&valid_request).is_ok());
let invalid_request = GraphQLRequest::new("{ invalid syntax }}}");
assert!(adapter.validate_request(&invalid_request).is_err());
}
#[test]
fn test_graphiql_source() {
let adapter = GraphQLProductionAdapter::new("/graphql");
let html = adapter.graphiql_source();
assert!(html.contains("GraphiQL"));
assert!(html.contains("/graphql"));
}
}