grafbase_sdk_mock/
server.rs

1use std::{
2    net::{Ipv4Addr, SocketAddrV4},
3    sync::Arc,
4};
5
6use async_graphql_axum::{GraphQLRequest, GraphQLResponse};
7use axum::{Router, extract::State, response::IntoResponse, routing::post};
8use url::Url;
9
10use super::DynamicSchema;
11
12/// Represents a mock GraphQL server used for testing purposes.
13pub struct MockGraphQlServer {
14    shutdown: Option<tokio::sync::oneshot::Sender<()>>,
15    state: AppState,
16    url: Url,
17    name: String,
18}
19
20impl Drop for MockGraphQlServer {
21    fn drop(&mut self) {
22        if let Some(shutdown) = self.shutdown.take() {
23            shutdown.send(()).ok();
24        }
25    }
26}
27
28#[derive(Clone)]
29struct AppState {
30    schema: Arc<DynamicSchema>,
31}
32
33impl MockGraphQlServer {
34    pub(crate) async fn new(name: String, schema: Arc<DynamicSchema>) -> Self {
35        let state = AppState { schema };
36
37        let app = Router::new()
38            .route("/", post(graphql_handler))
39            .with_state(state.clone());
40
41        let (shutdown_sender, shutdown_receiver) = tokio::sync::oneshot::channel::<()>();
42
43        let listen_address = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 0);
44        let listener = tokio::net::TcpListener::bind(listen_address).await.unwrap();
45
46        let listen_address = listener.local_addr().unwrap();
47
48        tokio::spawn(async move {
49            axum::serve(listener, app)
50                .with_graceful_shutdown(async move {
51                    shutdown_receiver.await.ok();
52                })
53                .await
54                .unwrap();
55        });
56
57        let url = format!("http://{listen_address}").parse().unwrap();
58
59        MockGraphQlServer {
60            shutdown: Some(shutdown_sender),
61            url,
62            state,
63            name,
64        }
65    }
66
67    /// Returns a reference to the URL of the mock GraphQL server
68    pub fn url(&self) -> &Url {
69        &self.url
70    }
71
72    /// Returns the Schema Definition Language representation of the underlying GraphQL schema
73    pub fn sdl(&self) -> &str {
74        self.state.schema.sdl()
75    }
76
77    /// Returns the name of the subgraph
78    pub fn name(&self) -> &str {
79        &self.name
80    }
81}
82
83async fn graphql_handler(State(state): State<AppState>, req: GraphQLRequest) -> axum::response::Response {
84    let req = req.into_inner();
85    let response: GraphQLResponse = state.schema.execute(req).await.into();
86
87    response.into_response()
88}