iota_graphql_rpc/
error.rs1use async_graphql::{ErrorExtensionValues, ErrorExtensions, Pos, Response, ServerError};
6use async_graphql_axum::GraphQLResponse;
7use iota_indexer::errors::IndexerError;
8use iota_names::error::IotaNamesError;
9
10pub(crate) mod code {
14    pub const BAD_REQUEST: &str = "BAD_REQUEST";
15    pub const BAD_USER_INPUT: &str = "BAD_USER_INPUT";
16    pub const INTERNAL_SERVER_ERROR: &str = "INTERNAL_SERVER_ERROR";
17    pub const REQUEST_TIMEOUT: &str = "REQUEST_TIMEOUT";
18    pub const UNKNOWN: &str = "UNKNOWN";
19}
20
21pub(crate) fn graphql_error_response(code: &str, message: impl Into<String>) -> GraphQLResponse {
29    let error = graphql_error(code, message);
30    Response::from_errors(error.into()).into()
31}
32
33pub(crate) fn graphql_error(code: &str, message: impl Into<String>) -> ServerError {
38    let mut ext = ErrorExtensionValues::default();
39    ext.set("code", code);
40
41    ServerError {
42        message: message.into(),
43        source: None,
44        locations: vec![],
45        path: vec![],
46        extensions: Some(ext),
47    }
48}
49
50pub(crate) fn graphql_error_at_pos(
51    code: &str,
52    message: impl Into<String>,
53    pos: Pos,
54) -> ServerError {
55    let mut ext = ErrorExtensionValues::default();
56    ext.set("code", code);
57
58    ServerError {
59        message: message.into(),
60        source: None,
61        locations: vec![pos],
62        path: vec![],
63        extensions: Some(ext),
64    }
65}
66
67#[derive(Clone, Debug, thiserror::Error)]
68#[non_exhaustive]
69pub enum Error {
70    #[error("Unsupported protocol version requested. Min supported: {0}, max supported: {1}")]
71    ProtocolVersionUnsupported(u64, u64),
72    #[error("'first' and 'last' must not be used together")]
73    CursorNoFirstLast,
74    #[error("Connection's page size of {0} exceeds max of {1}")]
75    PageTooLarge(u64, u32),
76    #[error("{0}")]
78    Client(String),
79    #[error("Internal error occurred while processing request: {0}")]
80    Internal(String),
81    #[error(transparent)]
82    IotaNames(#[from] IotaNamesError),
83    #[error("{0}")]
84    ServerInit(String),
85    #[error("Unsupported feature: {0}")]
86    UnsupportedFeature(String),
87}
88
89impl ErrorExtensions for Error {
90    fn extend(&self) -> async_graphql::Error {
91        async_graphql::Error::new(format!("{self}")).extend_with(|_err, e| match self {
92            Error::CursorNoFirstLast
93            | Error::PageTooLarge(_, _)
94            | Error::ProtocolVersionUnsupported(_, _)
95            | Error::Client(_) => {
96                e.set("code", code::BAD_USER_INPUT);
97            }
98            Error::Internal(_) => {
99                e.set("code", code::INTERNAL_SERVER_ERROR);
100            }
101            Error::IotaNames(_) => {
102                e.set("code", code::BAD_REQUEST);
103            }
104            Error::ServerInit(_) => {
105                e.set("code", code::UNKNOWN);
106            }
107            Error::UnsupportedFeature(_) => {
108                e.set("code", code::BAD_REQUEST);
109            }
110        })
111    }
112}
113
114impl From<IndexerError> for Error {
115    fn from(e: IndexerError) -> Self {
116        Error::Internal(e.to_string())
117    }
118}