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}