grafbase_sdk_mock/
server.rs1use std::{
2 net::{Ipv4Addr, SocketAddrV4},
3 sync::Arc,
4};
5
6use async_graphql_axum::{GraphQLRequest, GraphQLResponse};
7use axum::{extract::State, response::IntoResponse, routing::post, Router};
8use url::Url;
9
10use super::DynamicSchema;
11
12pub 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 pub fn url(&self) -> &Url {
69 &self.url
70 }
71
72 pub fn sdl(&self) -> &str {
74 self.state.schema.sdl()
75 }
76
77 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}