hotshot_query_service/explorer/
errors.rs

1// Copyright (c) 2022 Espresso Systems (espressosys.com)
2// This file is part of the HotShot Query Service library.
3//
4// This program is free software: you can redistribute it and/or modify it under the terms of the GNU
5// General Public License as published by the Free Software Foundation, either version 3 of the
6// License, or (at your option) any later version.
7// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
8// even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9// General Public License for more details.
10// You should have received a copy of the GNU General Public License along with this program. If not,
11// see <https://www.gnu.org/licenses/>.
12
13use std::fmt::{Debug, Display};
14
15use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer};
16use tide_disco::StatusCode;
17
18/// [ExplorerAPIError] is a trait that represents an error that can be returned
19/// returned from the ExplorerAPI for various reasons.
20///
21/// It aims to be Serializable for the purposes of conveying the error to the
22/// client.
23pub trait ExplorerAPIError: Display + Debug {
24    /// The code for this error will uniquely identify this specific error.
25    ///
26    /// > This value **SHOULD** match the rename field for the `serde` tag, if applicable.
27    fn code(&self) -> &str;
28}
29
30impl std::error::Error for dyn ExplorerAPIError {
31    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
32        None
33    }
34
35    fn description(&self) -> &str {
36        "description() is deprecated; use Display"
37    }
38
39    fn cause(&self) -> Option<&dyn std::error::Error> {
40        self.source()
41    }
42}
43
44/// [Unimplemented] is an error that indicates that the feature in question is
45/// no implemented, but in addition, that this status can be conveyed to the
46/// called with serializable error code.
47#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
48#[serde(tag = "code", rename = "UNIMPLEMENTED")]
49pub struct Unimplemented {}
50
51impl Unimplemented {
52    pub fn status(&self) -> StatusCode {
53        StatusCode::NOT_IMPLEMENTED
54    }
55}
56
57impl ExplorerAPIError for Unimplemented {
58    fn code(&self) -> &str {
59        "UNIMPLEMENTED"
60    }
61}
62
63/// Implement [Display] for an error message.
64impl Display for Unimplemented {
65    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66        write!(f, "this feature is not yet implemented")
67    }
68}
69
70impl std::error::Error for Unimplemented {}
71
72impl Serialize for Unimplemented {
73    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
74    where
75        S: Serializer,
76    {
77        let mut st = serializer.serialize_struct("Unimplemented", 2)?;
78        st.serialize_field("code", &self.code())?;
79        st.serialize_field("message", &format!("{self}"))?;
80        st.end()
81    }
82}
83
84/// [InvalidLimit] is an error that represents that the there was a problem
85/// with the given limit parameter.
86#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
87#[serde(tag = "code", rename = "INVALID_LIMIT")]
88pub struct InvalidLimit {}
89
90impl InvalidLimit {
91    pub fn status(&self) -> StatusCode {
92        StatusCode::BAD_REQUEST
93    }
94}
95
96impl ExplorerAPIError for InvalidLimit {
97    fn code(&self) -> &str {
98        "INVALID_LIMIT"
99    }
100}
101
102impl Display for InvalidLimit {
103    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104        write!(
105            f,
106            "limit must be provided, and must be a positive integer less than 100"
107        )
108    }
109}
110
111impl std::error::Error for InvalidLimit {}
112
113impl Serialize for InvalidLimit {
114    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
115    where
116        S: Serializer,
117    {
118        let mut st = serializer.serialize_struct("InvalidLimit", 2)?;
119        st.serialize_field("code", &self.code())?;
120        st.serialize_field("message", &format!("{self}"))?;
121        st.end()
122    }
123}
124
125/// [NotFound] is an error that represents results could not be found for the
126/// given key, or "search" parameters.
127#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
128#[serde(tag = "code", rename = "NOT_FOUND")]
129pub struct NotFound {
130    pub key: String,
131}
132
133impl NotFound {
134    pub fn status(&self) -> StatusCode {
135        StatusCode::NOT_FOUND
136    }
137
138    pub fn key(&self) -> &str {
139        &self.key
140    }
141}
142
143impl ExplorerAPIError for NotFound {
144    fn code(&self) -> &str {
145        "NOT_FOUND"
146    }
147}
148
149impl Display for NotFound {
150    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151        write!(f, "no data found for key: '{}'", self.key)
152    }
153}
154
155impl std::error::Error for NotFound {}
156
157impl Serialize for NotFound {
158    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
159    where
160        S: Serializer,
161    {
162        let mut st = serializer.serialize_struct("NotFound", 2)?;
163        st.serialize_field("code", &self.code())?;
164        st.serialize_field("key", &self.key())?;
165        st.serialize_field("message", &format!("{self}"))?;
166        st.end()
167    }
168}
169
170/// QueryError is an error that indicates that a specific error occurred while
171/// evaluating a query, or decoding the results of a query.
172#[derive(Debug, Clone, Deserialize)]
173#[serde(tag = "code", rename = "QUERY_ERROR")]
174pub struct QueryError {
175    pub error: crate::QueryError,
176}
177
178impl QueryError {
179    pub fn status(&self) -> StatusCode {
180        self.error.status()
181    }
182
183    pub fn error(&self) -> &crate::QueryError {
184        &self.error
185    }
186}
187
188impl ExplorerAPIError for QueryError {
189    fn code(&self) -> &str {
190        "QUERY_ERROR"
191    }
192}
193
194impl Display for QueryError {
195    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196        write!(
197            f,
198            "encountered error attempting to retrieve data: '{}'",
199            self.error
200        )
201    }
202}
203
204impl std::error::Error for QueryError {
205    fn cause(&self) -> Option<&dyn std::error::Error> {
206        Some(&self.error)
207    }
208}
209
210impl Serialize for QueryError {
211    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
212    where
213        S: Serializer,
214    {
215        let mut st = serializer.serialize_struct("QueryError", 2)?;
216        st.serialize_field("code", &self.code())?;
217        st.serialize_field("error", &self.error())?;
218        st.serialize_field("message", &format!("{self}"))?;
219        st.end()
220    }
221}
222
223impl From<crate::QueryError> for QueryError {
224    fn from(error: crate::QueryError) -> Self {
225        Self { error }
226    }
227}
228
229/// BadQuery is an error that indicates a submitted query to the service is
230/// invalid, or malformed.
231#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
232#[serde(tag = "code", rename = "BAD_QUERY")]
233pub struct BadQuery {}
234
235impl BadQuery {
236    pub fn status(&self) -> StatusCode {
237        StatusCode::BAD_REQUEST
238    }
239}
240
241impl ExplorerAPIError for BadQuery {
242    fn code(&self) -> &str {
243        "BAD_QUERY"
244    }
245}
246
247impl Display for BadQuery {
248    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
249        write!(f, "provided query is invalid, or malformed")
250    }
251}
252
253impl std::error::Error for BadQuery {}
254
255impl Serialize for BadQuery {
256    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
257    where
258        S: Serializer,
259    {
260        let mut st = serializer.serialize_struct("BadQuery", 2)?;
261        st.serialize_field("code", &self.code())?;
262        st.serialize_field("message", &format!("{self}"))?;
263        st.end()
264    }
265}
266
267#[cfg(test)]
268mod test {
269    use super::{BadQuery, InvalidLimit, NotFound, QueryError, Unimplemented};
270
271    #[test]
272    fn test_serialize_deserialize_unimplemented() {
273        let unimplemented = Unimplemented {};
274        let serialize_result = serde_json::to_string(&unimplemented);
275        assert!(
276            serialize_result.is_ok(),
277            "failed to serialize Unimplemented: {}",
278            serialize_result.err().unwrap(),
279        );
280        let serialized = serialize_result.unwrap();
281
282        {
283            let have = &serialized;
284            let want =
285                r#"{"code":"UNIMPLEMENTED","message":"this feature is not yet implemented"}"#;
286            assert_eq!(
287                have, want,
288                "serialized Unimplemented mismatch: have: {have}, want: {want}"
289            );
290        }
291
292        let deserialize_result: Result<Unimplemented, _> = serde_json::from_str(&serialized);
293        assert!(
294            deserialize_result.is_ok(),
295            "failed to deserialize Unimplemented: {}",
296            deserialize_result.err().unwrap(),
297        );
298        let deserialized = deserialize_result.unwrap();
299        {
300            let have = deserialized;
301            let want = unimplemented;
302            assert_eq!(
303                have, want,
304                "deserialized Unimplemented mismatch: have: {have}, want: {want}"
305            );
306        }
307    }
308
309    #[test]
310    fn test_serialize_deserialize_invalid_limit() {
311        let invalid_limit = InvalidLimit {};
312        let serialize_result = serde_json::to_string(&invalid_limit);
313        assert!(
314            serialize_result.is_ok(),
315            "failed to serialize InvalidLimit: {}",
316            serialize_result.err().unwrap(),
317        );
318        let serialized = serialize_result.unwrap();
319
320        {
321            let have = &serialized;
322            let want = r#"{"code":"INVALID_LIMIT","message":"limit must be provided, and must be a positive integer less than 100"}"#;
323            assert_eq!(
324                have, want,
325                "serialized InvalidLimit mismatch: have: {have}, want: {want}"
326            );
327        }
328
329        let deserialize_result: Result<InvalidLimit, _> = serde_json::from_str(&serialized);
330        assert!(
331            deserialize_result.is_ok(),
332            "failed to deserialize InvalidLimit: {}",
333            deserialize_result.err().unwrap(),
334        );
335        let deserialized = deserialize_result.unwrap();
336        {
337            let have = deserialized;
338            let want = invalid_limit;
339            assert_eq!(
340                have, want,
341                "deserialized InvalidLimit mismatch: have: {have}, want: {want}"
342            );
343        }
344    }
345
346    #[test]
347    fn test_serialize_deserialize_not_found() {
348        let not_found = NotFound {
349            key: "foo".to_string(),
350        };
351        let serialize_result = serde_json::to_string(&not_found);
352        assert!(
353            serialize_result.is_ok(),
354            "failed to serialize NotFound: {}",
355            serialize_result.err().unwrap(),
356        );
357        let serialized = serialize_result.unwrap();
358
359        {
360            let have = &serialized;
361            let want =
362                r#"{"code":"NOT_FOUND","key":"foo","message":"no data found for key: 'foo'"}"#;
363            assert_eq!(
364                have, want,
365                "serialized NotFound mismatch: have: {have}, want: {want}"
366            );
367        }
368
369        let deserialize_result: Result<NotFound, _> = serde_json::from_str(&serialized);
370        assert!(
371            deserialize_result.is_ok(),
372            "failed to deserialize NotFound: {}",
373            deserialize_result.err().unwrap(),
374        );
375        let deserialized = deserialize_result.unwrap();
376        {
377            let have = deserialized;
378            let want = not_found;
379            assert_eq!(
380                have, want,
381                "deserialized NotFound mismatch: have: {have}, want: {want}"
382            );
383        }
384    }
385
386    #[test]
387    fn test_serialize_deserialize_query_error() {
388        let query_error = QueryError {
389            error: crate::QueryError::NotFound,
390        };
391        let serialize_result = serde_json::to_string(&query_error);
392        assert!(
393            serialize_result.is_ok(),
394            "failed to serialize QueryError: {}",
395            serialize_result.err().unwrap(),
396        );
397        let serialized = serialize_result.unwrap();
398
399        {
400            let have = &serialized;
401            let want = r#"{"code":"QUERY_ERROR","error":"NotFound","message":"encountered error attempting to retrieve data: 'The requested resource does not exist or is not known to this query service.'"}"#;
402            assert_eq!(
403                have, want,
404                "serialized QueryError mismatch: have: {have}, want: {want}"
405            );
406        }
407
408        let deserialize_result: Result<QueryError, _> = serde_json::from_str(&serialized);
409        assert!(
410            deserialize_result.is_ok(),
411            "failed to deserialize QueryError: {}",
412            deserialize_result.err().unwrap(),
413        );
414        let deserialized = deserialize_result.unwrap();
415        {
416            let have = deserialized;
417            let want = query_error;
418
419            match &have.error {
420                crate::QueryError::NotFound => {},
421                _ => panic!("deserialized QueryError mismatch: have: {have}, want: {want}"),
422            }
423        }
424    }
425
426    #[test]
427    fn test_serialize_deserialize_bad_query() {
428        let bad_query = BadQuery {};
429        let serialize_result = serde_json::to_string(&bad_query);
430        assert!(
431            serialize_result.is_ok(),
432            "failed to serialize BadQuery: {}",
433            serialize_result.err().unwrap(),
434        );
435        let serialized = serialize_result.unwrap();
436
437        {
438            let have = &serialized;
439            let want =
440                r#"{"code":"BAD_QUERY","message":"provided query is invalid, or malformed"}"#;
441            assert_eq!(
442                have, want,
443                "serialized BadQuery mismatch: have: {have}, want: {want}"
444            );
445        }
446
447        let deserialize_result: Result<BadQuery, _> = serde_json::from_str(&serialized);
448        assert!(
449            deserialize_result.is_ok(),
450            "failed to deserialize BadQuery: {}",
451            deserialize_result.err().unwrap(),
452        );
453        let deserialized = deserialize_result.unwrap();
454        {
455            let have = deserialized;
456            let want = bad_query;
457
458            assert_eq!(
459                have, want,
460                "deserialized BadQuery mismatch: have: {have}, want: {want}"
461            );
462        }
463    }
464}