Skip to main content

shopify_sdk/clients/graphql/
errors.rs

1//! GraphQL-specific error types for the Shopify API SDK.
2//!
3//! This module contains error types for GraphQL API operations, including
4//! wrapped HTTP errors.
5//!
6//! # Error Handling
7//!
8//! The SDK uses specific error types for different failure scenarios.
9//! For GraphQL, only HTTP-level errors are exposed. GraphQL-level errors
10//! (such as user errors or validation errors) are returned in the response
11//! body with HTTP status 200, and are the user's responsibility to parse.
12//!
13//! - [`GraphqlError::Http`]: Wraps underlying HTTP errors
14//!
15//! # Example
16//!
17//! ```rust,ignore
18//! use shopify_sdk::clients::graphql::{GraphqlClient, GraphqlError};
19//!
20//! match client.query("query { shop { name } }", None, None, None).await {
21//!     Ok(response) => {
22//!         // Check for GraphQL errors in response body
23//!         if let Some(errors) = response.body.get("errors") {
24//!             println!("GraphQL errors: {}", errors);
25//!         } else {
26//!             println!("Data: {}", response.body["data"]);
27//!         }
28//!     }
29//!     Err(GraphqlError::Http(e)) => {
30//!         println!("HTTP error: {}", e);
31//!     }
32//! }
33//! ```
34
35use crate::clients::HttpError;
36use thiserror::Error;
37
38/// Error type for GraphQL API operations.
39///
40/// This enum provides error types for GraphQL API operations,
41/// wrapping HTTP errors. Unlike REST errors, GraphQL does not need
42/// path validation or API-disabled variants.
43///
44/// Note that GraphQL-level errors (like user errors, validation errors)
45/// are returned with HTTP 200 status and are contained in the response
46/// body's `errors` field. These are not treated as SDK errors.
47///
48/// # Example
49///
50/// ```rust
51/// use shopify_sdk::clients::graphql::GraphqlError;
52/// use shopify_sdk::clients::{HttpError, HttpResponseError};
53///
54/// // HTTP error wrapping
55/// let http_error = HttpError::Response(HttpResponseError {
56///     code: 401,
57///     message: r#"{"error":"Unauthorized"}"#.to_string(),
58///     error_reference: None,
59/// });
60/// let graphql_error: GraphqlError = http_error.into();
61/// assert!(graphql_error.to_string().contains("Unauthorized"));
62/// ```
63#[derive(Debug, Error)]
64pub enum GraphqlError {
65    /// An HTTP-level error occurred.
66    ///
67    /// This variant wraps [`HttpError`] for unified error handling.
68    /// It includes network errors, non-2xx responses, and retry exhaustion.
69    #[error(transparent)]
70    Http(#[from] HttpError),
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76    use crate::clients::{HttpResponseError, MaxHttpRetriesExceededError};
77
78    #[test]
79    fn test_graphql_error_http_variant_wraps_http_error() {
80        let http_error = HttpError::Response(HttpResponseError {
81            code: 404,
82            message: r#"{"error":"Not Found"}"#.to_string(),
83            error_reference: Some("abc-123".to_string()),
84        });
85
86        let graphql_error = GraphqlError::Http(http_error);
87        let message = graphql_error.to_string();
88
89        assert!(message.contains("Not Found"));
90    }
91
92    #[test]
93    fn test_all_error_variants_implement_std_error() {
94        // Http variant
95        let http_error: &dyn std::error::Error =
96            &GraphqlError::Http(HttpError::Response(HttpResponseError {
97                code: 400,
98                message: "test".to_string(),
99                error_reference: None,
100            }));
101        let _ = http_error;
102    }
103
104    #[test]
105    fn test_from_http_error_conversion() {
106        let http_error = HttpError::Response(HttpResponseError {
107            code: 500,
108            message: r#"{"error":"Internal Server Error"}"#.to_string(),
109            error_reference: None,
110        });
111
112        // Test From<HttpError> conversion
113        let graphql_error: GraphqlError = http_error.into();
114
115        assert!(matches!(graphql_error, GraphqlError::Http(_)));
116    }
117
118    #[test]
119    fn test_http_error_wraps_max_retries_exceeded() {
120        let http_error = HttpError::MaxRetries(MaxHttpRetriesExceededError {
121            code: 429,
122            tries: 3,
123            message: r#"{"error":"Rate limited"}"#.to_string(),
124            error_reference: None,
125        });
126
127        let graphql_error = GraphqlError::Http(http_error);
128        let message = graphql_error.to_string();
129
130        assert!(message.contains("Exceeded maximum retry count"));
131        assert!(message.contains("3"));
132    }
133}