apollo_federation/connectors/runtime/
errors.rs

1use serde::Serialize;
2use serde_json_bytes::ByteString;
3use serde_json_bytes::Map;
4use serde_json_bytes::Value;
5
6use crate::connectors::Connector;
7use crate::connectors::runtime::key::ResponseKey;
8
9#[derive(Clone, Debug, Serialize)]
10pub struct RuntimeError {
11    pub message: String,
12    code: Option<String>,
13    pub coordinate: Option<String>,
14    pub subgraph_name: Option<String>,
15    pub path: String,
16    pub extensions: Map<ByteString, Value>,
17}
18
19impl RuntimeError {
20    pub fn new(message: impl Into<String>, response_key: &ResponseKey) -> Self {
21        Self {
22            message: message.into(),
23            code: None,
24            coordinate: None,
25            subgraph_name: None,
26            path: response_key.path_string(),
27            extensions: Default::default(),
28        }
29    }
30
31    pub fn extensions(&self) -> Map<ByteString, Value> {
32        let mut extensions = Map::default();
33        extensions
34            .entry("code")
35            .or_insert_with(|| self.code().into());
36        if let Some(subgraph_name) = &self.subgraph_name {
37            extensions
38                .entry("service")
39                .or_insert_with(|| Value::String(subgraph_name.clone().into()));
40        };
41
42        if let Some(coordinate) = &self.coordinate {
43            extensions.entry("connector").or_insert_with(|| {
44                Value::Object(Map::from_iter([(
45                    "coordinate".into(),
46                    Value::String(coordinate.to_string().into()),
47                )]))
48            });
49        }
50
51        extensions.extend(self.extensions.clone());
52        extensions
53    }
54
55    pub fn extension<K, V>(mut self, key: K, value: V) -> Self
56    where
57        K: Into<ByteString>,
58        V: Into<Value>,
59    {
60        self.extensions.insert(key.into(), value.into());
61        self
62    }
63
64    pub fn with_code(mut self, code: impl Into<String>) -> Self {
65        self.code = Some(code.into());
66        self
67    }
68
69    pub fn code(&self) -> &str {
70        self.code.as_deref().unwrap_or("CONNECTORS_FETCH")
71    }
72}
73
74/// An error sending a connector request. This represents a problem with sending the request
75/// to the connector, rather than an error returned from the connector itself.
76#[derive(Debug, Clone, thiserror::Error)]
77pub enum Error {
78    #[error("Request limit exceeded")]
79    RequestLimitExceeded,
80
81    #[error("Rate limit exceeded")]
82    RateLimited,
83
84    #[error("Gateway timeout")]
85    GatewayTimeout,
86
87    #[error("Connector error: {0}")]
88    TransportFailure(String),
89}
90
91impl Error {
92    pub fn to_runtime_error(
93        &self,
94        connector: &Connector,
95        response_key: &ResponseKey,
96    ) -> RuntimeError {
97        RuntimeError {
98            message: self.to_string(),
99            code: Some(self.code().to_string()),
100            coordinate: Some(connector.id.coordinate()),
101            subgraph_name: Some(connector.id.subgraph_name.clone()),
102            path: response_key.path_string(),
103            extensions: Default::default(),
104        }
105    }
106
107    pub fn code(&self) -> &'static str {
108        match self {
109            Self::RequestLimitExceeded => "REQUEST_LIMIT_EXCEEDED",
110            Self::RateLimited => "REQUEST_RATE_LIMITED",
111            Self::GatewayTimeout => "GATEWAY_TIMEOUT",
112            Self::TransportFailure(_) => "HTTP_CLIENT_ERROR",
113        }
114    }
115}