subgraph_mock/
lib.rs

1use handle::handle_request;
2use hyper::service::service_fn;
3use hyper_util::{
4    rt::{TokioExecutor, TokioIo},
5    server::conn::auto::Builder,
6};
7use state::{Config, State, default_port};
8use std::{fs, net::SocketAddr, path::PathBuf, sync::Arc};
9use tokio::net::TcpListener;
10use tracing::{error, info};
11
12pub mod handle;
13pub mod latency;
14pub mod state;
15
16/// A general purpose subgraph mock.
17#[derive(Debug, clap::Parser)]
18#[clap(about, name = "subgraph-mock", long_about = None)]
19pub struct Args {
20    /// Path to the config file that should be used to configure the server
21    #[arg(short, long)]
22    pub config: Option<PathBuf>,
23
24    /// Path to the supergraph SDL that the server should mock
25    #[arg(short, long)]
26    pub schema: PathBuf,
27}
28
29impl Args {
30    /// Load and initialise the configuration based on command line args
31    pub fn init(self) -> anyhow::Result<(u16, State)> {
32        let (port, config) = match self.config {
33            Some(path) => {
34                info!(path=%path.display(), "loading and parsing config file");
35                Config::parse_yaml(serde_yaml::from_slice(&fs::read(path)?)?)?
36            }
37            None => {
38                info!("using default config");
39                (default_port(), Config::default())
40            }
41        };
42
43        Ok((port, State::new(config, self.schema)?))
44    }
45}
46
47/// Run the server loop with the provided [State]
48pub async fn mock_server_loop(port: u16, state: State) -> anyhow::Result<()> {
49    let listener = TcpListener::bind(SocketAddr::from(([0, 0, 0, 0], port))).await?;
50    info!(%port, "subgraph mock server now listening");
51
52    let state = Arc::new(state);
53    loop {
54        let (stream, _) = listener.accept().await?;
55        let io = TokioIo::new(stream);
56
57        let state = state.clone();
58        tokio::spawn(async move {
59            if let Err(err) = Builder::new(TokioExecutor::new())
60                .serve_connection(io, service_fn(|req| handle_request(req, state.clone())))
61                .await
62            {
63                error!(%err, "server error");
64            }
65        });
66    }
67}