allframe_core/router/
graphql_prod.rs1use std::{future::Future, pin::Pin};
7
8#[cfg(feature = "router-graphql")]
9use async_graphql::{
10 http::GraphiQLSource, parser::parse_query, Error as GraphQLError, Request as GraphQLRequest,
11};
12
13use super::ProtocolAdapter;
14
15#[cfg(feature = "router-graphql")]
24pub struct GraphQLProductionAdapter {
25 playground_endpoint: String,
26}
27
28#[cfg(feature = "router-graphql")]
29impl GraphQLProductionAdapter {
30 pub fn new(playground_endpoint: impl Into<String>) -> Self {
32 Self {
33 playground_endpoint: playground_endpoint.into(),
34 }
35 }
36
37 pub fn parse_query(query: &str) -> Result<(), GraphQLError> {
39 parse_query(query).map(|_| ()).map_err(|e| e.into())
40 }
41
42 pub fn graphiql_source(&self) -> String {
44 GraphiQLSource::build()
45 .endpoint(&self.playground_endpoint)
46 .finish()
47 }
48
49 pub fn validate_request(&self, request: &GraphQLRequest) -> Result<(), String> {
51 Self::parse_query(request.query.as_str())
53 .map_err(|e| format!("Invalid GraphQL query: {:?}", e))
54 }
55}
56
57#[cfg(feature = "router-graphql")]
58impl ProtocolAdapter for GraphQLProductionAdapter {
59 fn name(&self) -> &str {
60 "graphql-production"
61 }
62
63 fn handle(
64 &self,
65 request: &str,
66 ) -> Pin<Box<dyn Future<Output = Result<String, String>> + Send + '_>> {
67 let request = request.to_string();
68 Box::pin(async move {
69 let _graphql_request = GraphQLRequest::new(&request);
71
72 match Self::parse_query(&request) {
74 Ok(_) => {
75 Ok(
77 r#"{"data":{"message":"Query parsed and validated successfully"}}"#
78 .to_string(),
79 )
80 }
81 Err(e) => Err(format!("GraphQL parsing error: {:?}", e)),
82 }
83 })
84 }
85}
86
87#[cfg(test)]
88#[cfg(feature = "router-graphql")]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn test_graphql_query_parsing() {
94 let valid_query = "{ user(id: 42) { name email } }";
96 assert!(GraphQLProductionAdapter::parse_query(valid_query).is_ok());
97
98 let invalid_query = "{ user(id: ) }";
100 assert!(GraphQLProductionAdapter::parse_query(invalid_query).is_err());
101 }
102
103 #[test]
104 fn test_graphql_mutation_parsing() {
105 let mutation = r#"
106 mutation CreateUser($name: String!) {
107 createUser(name: $name) {
108 id
109 name
110 }
111 }
112 "#;
113 assert!(GraphQLProductionAdapter::parse_query(mutation).is_ok());
114 }
115
116 #[test]
117 fn test_graphql_subscription_parsing() {
118 let subscription = r#"
119 subscription OnUserCreated {
120 userCreated {
121 id
122 name
123 }
124 }
125 "#;
126 assert!(GraphQLProductionAdapter::parse_query(subscription).is_ok());
127 }
128
129 #[tokio::test]
130 async fn test_adapter_validation() {
131 let adapter = GraphQLProductionAdapter::new("/graphql");
132
133 let valid_request = GraphQLRequest::new("{ hello }");
135 assert!(adapter.validate_request(&valid_request).is_ok());
136
137 let invalid_request = GraphQLRequest::new("{ invalid syntax }}}");
139 assert!(adapter.validate_request(&invalid_request).is_err());
140 }
141
142 #[test]
143 fn test_graphiql_source() {
144 let adapter = GraphQLProductionAdapter::new("/graphql");
145 let html = adapter.graphiql_source();
146 assert!(html.contains("GraphiQL"));
147 assert!(html.contains("/graphql"));
148 }
149}