autumn_plugin_shopify/
error.rs1use autumn_web::AutumnError;
11use autumn_web::reexports::http::StatusCode;
12use thiserror::Error;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16#[non_exhaustive]
17pub enum ErrorKind {
18 Config,
20 Signature,
22 InvalidRequest,
24 PayloadTooLarge,
26 Json,
28 Http,
30 ShopifyApi,
32 GraphQl,
34 Throttled,
36 UnexpectedResponse,
38 InProgress,
40 StateMismatch,
42 ScopeMismatch,
44 NotInstalled,
46 Reauthorize,
48 OAuthDisabled,
50 Store,
52 MissingRuntime,
54}
55
56#[derive(Debug, Error)]
58#[non_exhaustive]
59pub enum Error {
60 #[error("shopify config error: {0}")]
62 Config(String),
63
64 #[error("shopify signature error: {0}")]
66 Signature(String),
67
68 #[error("shopify invalid request: {0}")]
70 InvalidRequest(String),
71
72 #[error("shopify request body is larger than {limit} bytes")]
74 PayloadTooLarge {
75 limit: usize,
77 },
78
79 #[error("shopify json error: {0}")]
81 Json(#[from] serde_json::Error),
82
83 #[error("shopify http error: {0}")]
85 Http(#[from] reqwest::Error),
86
87 #[error("shopify api error: status {status}: {body}")]
89 ShopifyApi {
90 status: u16,
92 body: String,
94 },
95
96 #[error("shopify graphql error: {}", .0.join("; "))]
98 GraphQl(Vec<String>),
99
100 #[error("shopify throttled the request after {retries} retries")]
102 Throttled {
103 retries: u32,
105 },
106
107 #[error("shopify response has the wrong shape: {0}")]
109 UnexpectedResponse(String),
110
111 #[error("shopify webhook {0} is in progress")]
113 InProgress(String),
114
115 #[error("shopify oauth state does not match")]
117 StateMismatch,
118
119 #[error("shopify scopes not granted: {}", .missing.join(","))]
121 ScopeMismatch {
122 missing: Vec<String>,
124 },
125
126 #[error("shop {0} has no stored token; install the app")]
128 NotInstalled(String),
129
130 #[error("shop {0} must install the app again")]
132 Reauthorize(String),
133
134 #[error("shopify oauth is off; set [shopify] app_url")]
136 OAuthDisabled,
137
138 #[error("shopify store error: {0}")]
140 Store(String),
141
142 #[error("shopify runtime is missing; add ShopifyPlugin to the app")]
144 MissingRuntime,
145}
146
147impl Error {
148 #[must_use]
150 pub const fn kind(&self) -> ErrorKind {
151 match self {
152 Self::Config(_) => ErrorKind::Config,
153 Self::Signature(_) => ErrorKind::Signature,
154 Self::InvalidRequest(_) => ErrorKind::InvalidRequest,
155 Self::PayloadTooLarge { .. } => ErrorKind::PayloadTooLarge,
156 Self::Json(_) => ErrorKind::Json,
157 Self::Http(_) => ErrorKind::Http,
158 Self::ShopifyApi { .. } => ErrorKind::ShopifyApi,
159 Self::GraphQl(_) => ErrorKind::GraphQl,
160 Self::Throttled { .. } => ErrorKind::Throttled,
161 Self::UnexpectedResponse(_) => ErrorKind::UnexpectedResponse,
162 Self::InProgress(_) => ErrorKind::InProgress,
163 Self::StateMismatch => ErrorKind::StateMismatch,
164 Self::ScopeMismatch { .. } => ErrorKind::ScopeMismatch,
165 Self::NotInstalled(_) => ErrorKind::NotInstalled,
166 Self::Reauthorize(_) => ErrorKind::Reauthorize,
167 Self::OAuthDisabled => ErrorKind::OAuthDisabled,
168 Self::Store(_) => ErrorKind::Store,
169 Self::MissingRuntime => ErrorKind::MissingRuntime,
170 }
171 }
172
173 #[must_use]
175 pub const fn status(&self) -> StatusCode {
176 match self.kind() {
177 ErrorKind::Signature | ErrorKind::NotInstalled | ErrorKind::Reauthorize => {
178 StatusCode::UNAUTHORIZED
179 }
180 ErrorKind::InvalidRequest | ErrorKind::Json => StatusCode::BAD_REQUEST,
181 ErrorKind::PayloadTooLarge => StatusCode::PAYLOAD_TOO_LARGE,
182 ErrorKind::StateMismatch | ErrorKind::ScopeMismatch => StatusCode::FORBIDDEN,
183 ErrorKind::OAuthDisabled => StatusCode::NOT_FOUND,
184 ErrorKind::InProgress => StatusCode::CONFLICT,
185 ErrorKind::Http
186 | ErrorKind::ShopifyApi
187 | ErrorKind::GraphQl
188 | ErrorKind::UnexpectedResponse => StatusCode::BAD_GATEWAY,
189 ErrorKind::Throttled | ErrorKind::MissingRuntime => StatusCode::SERVICE_UNAVAILABLE,
190 ErrorKind::Config | ErrorKind::Store => StatusCode::INTERNAL_SERVER_ERROR,
191 }
192 }
193
194 #[must_use]
197 pub fn to_autumn_error(&self) -> AutumnError {
198 AutumnError::internal_server_error_msg(self.to_string()).with_status(self.status())
199 }
200}
201
202pub type Result<T> = std::result::Result<T, Error>;
204
205#[cfg(test)]
206mod tests;