1use crate::error::{
8 DnsError, DrandError, GovernanceError, IdentityError, NamesError, NetworkClientError,
9 PublishError, RegistrationError, ResolutionError, StorageError, VdfError,
10};
11use serde::{Deserialize, Serialize};
12
13#[derive(Debug, Serialize, Deserialize, Clone)]
15pub struct ApiError {
16 #[serde(rename = "type")]
18 pub error_type: String,
19 pub title: String,
21 pub status: u16,
23 pub detail: String,
25 #[serde(skip_serializing_if = "Option::is_none")]
27 pub instance: Option<String>,
28 pub code: String,
30 pub retryable: bool,
32 #[serde(skip_serializing_if = "serde_json::Value::is_null")]
34 pub details: serde_json::Value,
35 pub request_id: String,
37}
38
39impl ApiError {
40 pub fn http_status(&self) -> u16 {
42 self.status
43 }
44}
45
46fn current_request_id() -> String {
47 crate::request_id::current().to_string()
48}
49
50impl From<ResolutionError> for ApiError {
51 fn from(e: ResolutionError) -> Self {
52 let (status, title): (u16, &'static str) = match &e {
53 ResolutionError::Offline => (503, "Node Offline"),
54 ResolutionError::NotFound { .. } => (404, "Name Not Found"),
55 ResolutionError::VdfVerificationFailed { .. } => {
56 (422, "Cryptographic Verification Failed")
57 }
58 ResolutionError::Expired { .. } => (410, "Registration Expired"),
59 ResolutionError::Timeout { .. } => (504, "Resolution Timeout"),
60 ResolutionError::Internal { .. } => (500, "Internal Resolution Error"),
61 };
62 ApiError {
63 error_type: e.error_type_uri(),
64 title: title.to_string(),
65 status,
66 detail: e.user_message(),
67 instance: None,
68 code: e.code().to_string(),
69 retryable: e.is_retryable(),
70 details: e.details(),
71 request_id: current_request_id(),
72 }
73 }
74}
75
76impl From<PublishError> for ApiError {
77 fn from(e: PublishError) -> Self {
78 let (status, title): (u16, &'static str) = match &e {
79 PublishError::Offline => (503, "Node Offline"),
80 PublishError::InvalidProof(_) => (400, "Invalid VDF Proof"),
81 PublishError::AlreadyOwned { .. } => (409, "Name Already Owned"),
82 PublishError::AllFailed { .. } => (503, "Publish Failed"),
83 PublishError::Rejected(_) => (422, "Publish Rejected"),
84 PublishError::Internal { .. } => (500, "Internal Publish Error"),
85 };
86 ApiError {
87 error_type: e.error_type_uri(),
88 title: title.to_string(),
89 status,
90 detail: e.user_message(),
91 instance: None,
92 code: e.code().to_string(),
93 retryable: e.is_retryable(),
94 details: e.details(),
95 request_id: current_request_id(),
96 }
97 }
98}
99
100impl From<RegistrationError> for ApiError {
101 fn from(e: RegistrationError) -> Self {
102 let (status, title): (u16, &'static str) = match &e {
103 RegistrationError::InvalidName { .. } => (400, "Invalid Name"),
104 RegistrationError::VdfFailed(_) => (500, "VDF Computation Failed"),
105 RegistrationError::CommitmentMismatch => (422, "Commitment Mismatch"),
106 RegistrationError::AlreadyOwned { .. } => (409, "Name Already Owned"),
107 RegistrationError::AlreadyInProgress { .. } => (409, "Registration In Progress"),
108 RegistrationError::NetworkRejected { .. } => (422, "Registration Rejected"),
109 RegistrationError::Internal { .. } => (500, "Internal Registration Error"),
110 };
111 ApiError {
112 error_type: e.error_type_uri(),
113 title: title.to_string(),
114 status,
115 detail: e.user_message(),
116 instance: None,
117 code: e.code().to_string(),
118 retryable: e.is_retryable(),
119 details: e.details(),
120 request_id: current_request_id(),
121 }
122 }
123}
124
125impl From<GovernanceError> for ApiError {
126 fn from(e: GovernanceError) -> Self {
127 let (status, title): (u16, &'static str) = match &e {
128 GovernanceError::MissingRootKey => (500, "Configuration Error"),
129 GovernanceError::StaleProposal
130 | GovernanceError::TimelockNotExpired
131 | GovernanceError::NotPendingOrVetoed => (409, "Conflict"),
132 GovernanceError::InsufficientSignatures => (401, "Unauthorized"),
133 GovernanceError::GovernanceDisabled => (403, "Forbidden"),
134 GovernanceError::KeyLengthMismatch | GovernanceError::InvalidPremiumNameLength | GovernanceError::InvalidInfrastructureName => {
135 (400, "Bad Request")
136 }
137 };
138 ApiError {
139 error_type: e.error_type_uri(),
140 title: title.to_string(),
141 status,
142 detail: e.user_message(),
143 instance: None,
144 code: e.code().to_string(),
145 retryable: e.is_retryable(),
146 details: serde_json::Value::Null,
147 request_id: current_request_id(),
148 }
149 }
150}
151
152impl From<NetworkClientError> for ApiError {
153 fn from(e: NetworkClientError) -> Self {
154 let (status, title): (u16, &'static str) = match &e {
155 NetworkClientError::Timeout | NetworkClientError::StreamDropped => {
156 (504, "Gateway Timeout")
157 }
158 NetworkClientError::Offline | NetworkClientError::RoutingTableEmpty => {
159 (503, "Service Unavailable")
160 }
161 NetworkClientError::ChannelClosed
162 | NetworkClientError::StoreError(_)
163 | NetworkClientError::Other(_) => (500, "Internal Server Error"),
164 NetworkClientError::UnsupportedProtocol => (501, "Not Implemented"),
165 NetworkClientError::GossipSubError(_) => (502, "Bad Gateway"),
166 };
167 ApiError {
168 error_type: e.error_type_uri(),
169 title: title.to_string(),
170 status,
171 detail: e.user_message(),
172 instance: None,
173 code: e.code().to_string(),
174 retryable: e.is_retryable(),
175 details: serde_json::Value::Null,
176 request_id: current_request_id(),
177 }
178 }
179}
180
181impl From<StorageError> for ApiError {
182 fn from(e: StorageError) -> Self {
183 let (status, title): (u16, &'static str) = match &e {
184 StorageError::DatabaseLocked => (423, "Locked"),
185 StorageError::Corruption(_) => (500, "Storage Corruption"),
186 StorageError::OperationFailed(_) => (500, "Storage Operation Failed"),
187 };
188 ApiError {
189 error_type: e.error_type_uri(),
190 title: title.to_string(),
191 status,
192 detail: e.user_message(),
193 instance: None,
194 code: e.code().to_string(),
195 retryable: e.is_retryable(),
196 details: serde_json::Value::Null,
197 request_id: current_request_id(),
198 }
199 }
200}
201
202impl From<VdfError> for ApiError {
203 fn from(e: VdfError) -> Self {
204 let (status, title): (u16, &'static str) = match &e {
205 VdfError::LockFileError(_) | VdfError::LockAcquireError(_) => {
206 (503, "Service Unavailable")
207 }
208 VdfError::DiscriminantError | VdfError::ProofGenerationError => {
209 (500, "VDF Computation Error")
210 }
211 VdfError::UnsupportedPlatform => (501, "Not Implemented"),
212 VdfError::InvalidProof => (400, "Bad Request"),
213 };
214 ApiError {
215 error_type: e.error_type_uri(),
216 title: title.to_string(),
217 status,
218 detail: e.user_message(),
219 instance: None,
220 code: e.code().to_string(),
221 retryable: e.is_retryable(),
222 details: serde_json::Value::Null,
223 request_id: current_request_id(),
224 }
225 }
226}
227
228impl From<DrandError> for ApiError {
229 fn from(e: DrandError) -> Self {
230 let (status, title): (u16, &'static str) = match &e {
231 DrandError::AllEndpointsFailed | DrandError::Network(_) | DrandError::Reqwest(_) => {
232 (502, "Bad Gateway")
233 }
234 DrandError::HttpError(s) => (*s, "Upstream Error"),
235 DrandError::NoCachedKyn => (404, "Not Found"),
236 DrandError::Serde(_) | DrandError::Storage(_) => (500, "Internal Server Error"),
237 DrandError::InvalidSignature => (422, "Cryptographic Verification Failed"),
238 DrandError::StaleKyn { .. } => (400, "Stale Network Kyn"),
239 };
240 ApiError {
241 error_type: e.error_type_uri(),
242 title: title.to_string(),
243 status,
244 detail: e.user_message(),
245 instance: None,
246 code: e.code().to_string(),
247 retryable: e.is_retryable(),
248 details: serde_json::Value::Null,
249 request_id: current_request_id(),
250 }
251 }
252}
253
254impl From<DnsError> for ApiError {
255 fn from(e: DnsError) -> Self {
256 let (status, title): (u16, &'static str) = match &e {
257 DnsError::NestedTooDeeply
258 | DnsError::ParseError(_)
259 | DnsError::TooManyRecords
260 | DnsError::InvalidLabelLength(_)
261 | DnsError::InvalidLabelCharacters(_)
262 | DnsError::InvalidCnameConfiguration(_)
263 | DnsError::TxtRecordTooLong(_)
264 | DnsError::InvalidCnameTarget(_)
265 | DnsError::InvalidPeerId(_)
266 | DnsError::InvalidKid(_)
267 | DnsError::InvalidIpfsCid(_) => (400, "Bad Request"),
268 };
269 ApiError {
270 error_type: e.error_type_uri(),
271 title: title.to_string(),
272 status,
273 detail: e.user_message(),
274 instance: None,
275 code: e.code().to_string(),
276 retryable: e.is_retryable(),
277 details: serde_json::Value::Null,
278 request_id: current_request_id(),
279 }
280 }
281}
282
283impl From<IdentityError> for ApiError {
284 fn from(e: IdentityError) -> Self {
285 let (status, title): (u16, &'static str) = match &e {
286 IdentityError::Io(_) | IdentityError::CorruptedIdentityFile(_) => {
287 (500, "Internal Server Error")
288 }
289 IdentityError::IdentityNotFound(_) => (404, "Not Found"),
290 IdentityError::InvalidSeedPhrase(_) => (400, "Bad Request"),
291 IdentityError::DecryptionFailed(_) => (401, "Unauthorized"),
292 };
293 ApiError {
294 error_type: e.error_type_uri(),
295 title: title.to_string(),
296 status,
297 detail: e.user_message(),
298 instance: None,
299 code: e.code().to_string(),
300 retryable: e.is_retryable(),
301 details: serde_json::Value::Null,
302 request_id: current_request_id(),
303 }
304 }
305}
306
307impl From<NamesError> for ApiError {
308 fn from(e: NamesError) -> Self {
309 ApiError {
311 error_type: e.error_type_uri(),
312 title: "Invalid Name".to_string(),
313 status: 400,
314 detail: e.user_message(),
315 instance: None,
316 code: e.code().to_string(),
317 retryable: e.is_retryable(),
318 details: serde_json::Value::Null,
319 request_id: current_request_id(),
320 }
321 }
322}