graphgate_handler/
service_route.rs

1use std::collections::HashMap;
2use std::ops::{Deref, DerefMut};
3
4use futures_util::TryFutureExt;
5use graphgate_planner::{Request, Response};
6use http::HeaderMap;
7use once_cell::sync::Lazy;
8
9static HTTP_CLIENT: Lazy<reqwest::Client> = Lazy::new(Default::default);
10
11/// Service routing information.
12#[derive(Clone, Eq, PartialEq, Debug)]
13pub struct ServiceRoute {
14    /// Service address
15    ///
16    /// For example: 1.2.3.4:8000, example.com:8080
17    pub addr: String,
18
19    /// Use TLS
20    pub tls: bool,
21
22    /// GraphQL HTTP path, default is `/`.
23    pub query_path: Option<String>,
24
25    /// GraphQL WebSocket path, default is `/`.
26    pub subscribe_path: Option<String>,
27}
28
29/// Service routing table
30///
31/// The key is the service name.
32#[derive(Default, Debug, Clone, Eq, PartialEq)]
33pub struct ServiceRouteTable(HashMap<String, ServiceRoute>);
34
35impl Deref for ServiceRouteTable {
36    type Target = HashMap<String, ServiceRoute>;
37
38    fn deref(&self) -> &Self::Target {
39        &self.0
40    }
41}
42
43impl DerefMut for ServiceRouteTable {
44    fn deref_mut(&mut self) -> &mut Self::Target {
45        &mut self.0
46    }
47}
48
49impl ServiceRouteTable {
50    /// Call the GraphQL query of the specified service.
51    pub async fn query(
52        &self,
53        service: impl AsRef<str>,
54        request: Request,
55        header_map: Option<&HeaderMap>,
56    ) -> anyhow::Result<Response> {
57        let service = service.as_ref();
58        let route = self.0.get(service).ok_or_else(|| {
59            anyhow::anyhow!("Service '{}' is not defined in the routing table.", service)
60        })?;
61        let scheme = match route.tls {
62            true => "https",
63            false => "http",
64        };
65        let url = match &route.query_path {
66            Some(path) => format!("{}://{}{}", scheme, route.addr, path),
67            None => format!("{}://{}", scheme, route.addr),
68        };
69
70        let resp = HTTP_CLIENT
71            .post(&url)
72            .headers(header_map.cloned().unwrap_or_default())
73            .json(&request)
74            .send()
75            .and_then(|res| async move { res.error_for_status() })
76            .and_then(|res| res.json::<Response>())
77            .await?;
78        Ok(resp)
79    }
80}