1use crate::{
33 error::Error as ClientError,
34 http::{headers::HeaderMap, Method, StatusCode, Url},
35};
36use bytes::Bytes;
37use serde::{
38 de,
39 de::{DeserializeOwned, MapAccess, Visitor},
40 Deserialize, Deserializer, Serialize,
41};
42use serde_json::Value;
43use std::{collections::BTreeMap, fmt, str::FromStr};
44use void::Void;
45
46pub struct Response {
48 response: reqwest::Response,
49 method: Method,
50}
51
52impl Response {
53 pub fn new(response: reqwest::Response, method: Method) -> Self {
55 Self {
56 response: response,
57 method: method,
58 }
59 }
60
61 pub fn content_length(&self) -> Option<u64> {
69 self.response.content_length()
70 }
71
72 pub fn content_type(&self) -> &str {
74 self.response
75 .headers()
76 .get(crate::http::headers::CONTENT_TYPE)
77 .and_then(|value| value.to_str().ok())
78 .unwrap()
79 }
80
81 pub fn error_for_status_code(self) -> Result<Self, ClientError> {
83 match self.response.error_for_status_ref() {
84 Ok(_) => Ok(self),
85 Err(err) => Err(err.into()),
86 }
87 }
88
89 pub fn error_for_status_code_ref(&self) -> Result<&Self, ClientError> {
91 match self.response.error_for_status_ref() {
92 Ok(_) => Ok(self),
93 Err(err) => Err(err.into()),
94 }
95 }
96
97 pub async fn exception(self) -> Result<Option<Exception>, ClientError> {
102 if self.status_code().is_client_error() || self.status_code().is_server_error() {
103 let ex = self.json().await?;
104 Ok(Some(ex))
105 } else {
106 Ok(None)
107 }
108 }
109
110 pub async fn json<B>(self) -> Result<B, ClientError>
114 where
115 B: DeserializeOwned,
116 {
117 let body = self.response.json::<B>().await?;
118 Ok(body)
119 }
120
121 pub fn headers(&self) -> &HeaderMap {
123 self.response.headers()
124 }
125
126 pub fn method(&self) -> Method {
128 self.method
129 }
130
131 pub fn status_code(&self) -> StatusCode {
133 self.response.status()
134 }
135
136 pub async fn text(self) -> Result<String, ClientError> {
140 let body = self.response.text().await?;
141 Ok(body)
142 }
143
144 pub async fn bytes(self) -> Result<Bytes, ClientError> {
148 let bytes: Bytes = self.response.bytes().await?;
149 Ok(bytes)
150 }
151
152 pub fn url(&self) -> &Url {
154 self.response.url()
155 }
156
157 pub fn warning_headers(&self) -> impl Iterator<Item = &str> {
162 self.response.headers().get_all("Warning").iter().map(|w| {
163 let s = w.to_str().unwrap();
164 let first_quote = s.find('"').unwrap();
165 let last_quote = s.len() - 1;
166 &s[first_quote + 1..last_quote]
167 })
168 }
169}
170
171impl fmt::Debug for Response {
172 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
173 f.debug_struct("Response")
174 .field("method", &self.method())
175 .field("url", self.url())
176 .field("status_code", &self.status_code())
177 .field("headers", self.headers())
178 .finish()
179 }
180}
181
182#[serde_with::skip_serializing_none]
187#[derive(Debug, PartialEq, Deserialize, Serialize, Clone)]
188pub struct Exception {
189 status: Option<u16>,
190 #[serde(deserialize_with = "crate::string_or_struct")]
191 error: Error,
192}
193
194impl Exception {
195 pub fn status(&self) -> Option<u16> {
197 self.status
198 }
199
200 pub fn error(&self) -> &Error {
202 &self.error
203 }
204}
205
206#[serde_with::skip_serializing_none]
208#[derive(Debug, PartialEq, Deserialize, Serialize, Clone)]
209pub struct Error {
210 #[serde(deserialize_with = "option_box_cause", default)]
211 caused_by: Option<Box<Cause>>,
212 #[serde(default = "BTreeMap::new", deserialize_with = "header_map")]
213 header: BTreeMap<String, Vec<String>>,
214 #[serde(default = "Vec::new")]
215 root_cause: Vec<Cause>,
216 reason: Option<String>,
217 stack_trace: Option<String>,
218 #[serde(rename = "type")]
219 ty: Option<String>,
220 #[serde(default = "BTreeMap::new", flatten)]
221 additional_details: BTreeMap<String, Value>,
222}
223
224fn header_map<'de, D>(deserializer: D) -> Result<BTreeMap<String, Vec<String>>, D::Error>
226where
227 D: Deserializer<'de>,
228{
229 #[derive(Deserialize)]
230 struct Wrapper(#[serde(deserialize_with = "crate::string_or_seq_string")] Vec<String>);
231
232 let v: BTreeMap<String, Wrapper> = BTreeMap::deserialize(deserializer)?;
233 Ok(v.into_iter().map(|(k, Wrapper(v))| (k, v)).collect())
234}
235
236impl Error {
237 pub fn caused_by(&self) -> Option<&Cause> {
239 self.caused_by.as_deref()
240 }
241
242 pub fn root_cause(&self) -> &Vec<Cause> {
244 &self.root_cause
245 }
246
247 pub fn header(&self) -> &BTreeMap<String, Vec<String>> {
249 &self.header
250 }
251
252 pub fn reason(&self) -> Option<&str> {
254 self.reason.as_deref()
255 }
256
257 pub fn stack_trace(&self) -> Option<&str> {
261 self.stack_trace.as_deref()
262 }
263
264 pub fn ty(&self) -> Option<&str> {
266 self.ty.as_deref()
267 }
268
269 pub fn additional_details(&self) -> &BTreeMap<String, Value> {
274 &self.additional_details
275 }
276}
277
278impl FromStr for Error {
281 type Err = Void;
282
283 fn from_str(s: &str) -> Result<Self, Self::Err> {
284 Ok(Error {
285 caused_by: None,
286 header: Default::default(),
287 root_cause: Vec::new(),
288 reason: Some(s.to_string()),
289 stack_trace: None,
290 ty: None,
291 additional_details: Default::default(),
292 })
293 }
294}
295
296#[serde_with::skip_serializing_none]
298#[derive(Debug, PartialEq, Deserialize, Serialize, Clone)]
299pub struct Cause {
300 #[serde(deserialize_with = "option_box_cause", default)]
301 caused_by: Option<Box<Cause>>,
302 reason: Option<String>,
303 stack_trace: Option<String>,
304 #[serde(rename = "type")]
305 ty: Option<String>,
306 #[serde(default = "BTreeMap::new", flatten)]
307 additional_details: BTreeMap<String, Value>,
308}
309
310fn option_box_cause<'de, D>(deserializer: D) -> Result<Option<Box<Cause>>, D::Error>
314where
315 D: Deserializer<'de>,
316{
317 struct CauseVisitor;
318 impl<'de> Visitor<'de> for CauseVisitor {
319 type Value = Cause;
320
321 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
322 formatter.write_str("string or map")
323 }
324
325 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
326 where
327 E: de::Error,
328 {
329 Ok(Cause {
330 caused_by: None,
331 reason: Some(value.to_string()),
332 stack_trace: None,
333 ty: None,
334 additional_details: Default::default(),
335 })
336 }
337
338 fn visit_map<M>(self, map: M) -> Result<Self::Value, M::Error>
339 where
340 M: MapAccess<'de>,
341 {
342 Deserialize::deserialize(de::value::MapAccessDeserializer::new(map))
343 }
344 }
345
346 deserializer
347 .deserialize_any(CauseVisitor)
348 .map(|c| Some(Box::new(c)))
349}
350
351impl Cause {
352 pub fn caused_by(&self) -> Option<&Cause> {
354 self.caused_by.as_deref()
355 }
356
357 pub fn reason(&self) -> Option<&str> {
359 self.reason.as_deref()
360 }
361
362 pub fn stack_trace(&self) -> Option<&str> {
366 self.stack_trace.as_deref()
367 }
368
369 pub fn ty(&self) -> Option<&str> {
371 self.ty.as_deref()
372 }
373
374 pub fn additional_details(&self) -> &BTreeMap<String, Value> {
379 &self.additional_details
380 }
381}
382
383#[cfg(test)]
384pub mod tests {
385 use crate::http::response::Exception;
386 use serde_json::json;
387
388 #[test]
389 fn deserialize_error_string() -> Result<(), failure::Error> {
390 let json = r#"{"error":"no handler found for uri [/test_1/test/1/_update?_source=foo%2Cbar] and method [POST]"}"#;
391 let ex: Exception = serde_json::from_str(json)?;
392
393 assert_eq!(ex.status(), None);
394 assert_eq!(ex.error().reason(), Some("no handler found for uri [/test_1/test/1/_update?_source=foo%2Cbar] and method [POST]"));
395 assert_eq!(ex.error().ty(), None);
396
397 Ok(())
398 }
399
400 #[test]
401 fn deserialize_illegal_argument_exception() -> Result<(), failure::Error> {
402 let json = r#"{
403 "error": {
404 "root_cause": [{
405 "type": "illegal_argument_exception",
406 "reason": "Missing mandatory contexts in context query"
407 }],
408 "type": "search_phase_execution_exception",
409 "reason": "all shards failed",
410 "phase": "query",
411 "grouped": true,
412 "header": {
413 "WWW-Authenticate": "Bearer: token",
414 "x": ["y", "z"]
415 },
416 "failed_shards": [{
417 "shard": 0,
418 "index": "test",
419 "node": "APOkVK-rQi2Ll6CcAdeR6Q",
420 "reason": {
421 "type": "illegal_argument_exception",
422 "reason": "Missing mandatory contexts in context query"
423 }
424 }],
425 "caused_by": {
426 "type": "illegal_argument_exception",
427 "reason": "Missing mandatory contexts in context query",
428 "caused_by": {
429 "type": "illegal_argument_exception",
430 "reason": "Missing mandatory contexts in context query"
431 }
432 }
433 },
434 "status": 400
435 }"#;
436
437 let ex: Exception = serde_json::from_str(json)?;
438
439 assert_eq!(ex.status(), Some(400));
440
441 let error = ex.error();
442
443 assert_eq!(error.root_cause().len(), 1);
444 assert_eq!(
445 error.root_cause()[0].ty(),
446 Some("illegal_argument_exception")
447 );
448 assert_eq!(
449 error.root_cause()[0].reason(),
450 Some("Missing mandatory contexts in context query")
451 );
452
453 assert_eq!(error.header().len(), 2);
454 assert_eq!(
455 error.header().get("WWW-Authenticate"),
456 Some(&vec!["Bearer: token".to_string()])
457 );
458 assert_eq!(
459 error.header().get("x"),
460 Some(&vec!["y".to_string(), "z".to_string()])
461 );
462
463 assert!(error.caused_by().is_some());
464 let caused_by = error.caused_by().unwrap();
465
466 assert_eq!(caused_by.ty(), Some("illegal_argument_exception"));
467 assert_eq!(
468 caused_by.reason(),
469 Some("Missing mandatory contexts in context query")
470 );
471
472 assert!(caused_by.caused_by().is_some());
473 let caused_by_caused_by = caused_by.caused_by().unwrap();
474
475 assert_eq!(caused_by_caused_by.ty(), Some("illegal_argument_exception"));
476 assert_eq!(
477 caused_by_caused_by.reason(),
478 Some("Missing mandatory contexts in context query")
479 );
480
481 assert!(error.additional_details().len() > 0);
482 assert_eq!(
483 error.additional_details().get("phase"),
484 Some(&json!("query"))
485 );
486 assert_eq!(
487 error.additional_details().get("grouped"),
488 Some(&json!(true))
489 );
490
491 Ok(())
492 }
493
494 #[test]
495 fn deserialize_index_not_found_exception() -> Result<(), failure::Error> {
496 let json = r#"{
497 "error": {
498 "root_cause": [{
499 "type": "index_not_found_exception",
500 "reason": "no such index [test_index]",
501 "resource.type": "index_or_alias",
502 "resource.id": "test_index",
503 "index_uuid": "_na_",
504 "index": "test_index"
505 }],
506 "type": "index_not_found_exception",
507 "reason": "no such index [test_index]",
508 "resource.type": "index_or_alias",
509 "resource.id": "test_index",
510 "index_uuid": "_na_",
511 "index": "test_index"
512 },
513 "status": 404
514 }"#;
515
516 let ex: Exception = serde_json::from_str(json)?;
517
518 assert_eq!(ex.status(), Some(404));
519 let error = ex.error();
520
521 assert_eq!(error.ty(), Some("index_not_found_exception"));
522 assert_eq!(error.reason(), Some("no such index [test_index]"));
523 assert_eq!(
524 error.additional_details().get("index").unwrap(),
525 &json!("test_index")
526 );
527 assert_eq!(error.root_cause().len(), 1);
528 assert_eq!(
529 error.root_cause()[0].ty(),
530 Some("index_not_found_exception")
531 );
532 Ok(())
533 }
534}