iota_graphql_rpc_client/
response.rs1use std::{collections::BTreeMap, net::SocketAddr};
6
7use async_graphql::{Response, ServerError, Value};
8use iota_graphql_rpc_headers::VERSION_HEADER;
9use reqwest::{
10 Response as ReqwestResponse,
11 header::{HeaderMap, HeaderName},
12};
13use serde_json::json;
14
15use super::ClientError;
16
17#[derive(Debug)]
18pub struct GraphqlResponse {
19 headers: HeaderMap,
20 remote_address: Option<SocketAddr>,
21 http_version: reqwest::Version,
22 status: reqwest::StatusCode,
23 full_response: Response,
24}
25
26impl GraphqlResponse {
27 pub async fn from_resp(resp: ReqwestResponse) -> Result<Self, ClientError> {
28 let headers = resp.headers().clone();
29 let remote_address = resp.remote_addr();
30 let http_version = resp.version();
31 let status = resp.status();
32 let full_response: Response = resp.json().await.map_err(ClientError::InnerClient)?;
33
34 Ok(Self {
35 headers,
36 remote_address,
37 http_version,
38 status,
39 full_response,
40 })
41 }
42
43 #[expect(clippy::result_large_err)]
44 pub fn graphql_version(&self) -> Result<String, ClientError> {
45 Ok(self
46 .headers
47 .get(VERSION_HEADER.as_str())
48 .ok_or(ClientError::ServiceVersionHeaderNotFound)?
49 .to_str()
50 .map_err(|e| ClientError::ServiceVersionHeaderValueInvalidString { error: e })?
51 .to_string())
52 }
53
54 pub fn response_body(&self) -> &Response {
55 &self.full_response
56 }
57
58 pub fn response_body_json(&self) -> serde_json::Value {
59 json!(self.full_response)
60 }
61
62 pub fn response_body_json_pretty(&self) -> String {
63 serde_json::to_string_pretty(&self.full_response).unwrap()
64 }
65
66 pub fn http_status(&self) -> reqwest::StatusCode {
67 self.status
68 }
69
70 pub fn http_version(&self) -> reqwest::Version {
71 self.http_version
72 }
73
74 pub fn http_headers(&self) -> HeaderMap {
75 self.headers.clone()
76 }
77
78 pub fn http_headers_without_date(&self) -> HeaderMap {
81 let mut headers = self.http_headers().clone();
82 headers.remove(HeaderName::from_static("date"));
83 headers
84 }
85
86 pub fn remote_address(&self) -> Option<SocketAddr> {
87 self.remote_address
88 }
89
90 pub fn errors(&self) -> Vec<ServerError> {
91 self.full_response.errors.clone()
92 }
93
94 #[expect(clippy::result_large_err)]
95 pub fn usage(&self) -> Result<Option<BTreeMap<String, u64>>, ClientError> {
96 Ok(match self.full_response.extensions.get("usage").cloned() {
97 Some(Value::Object(obj)) => Some(
98 obj.into_iter()
99 .map(|(k, v)| match v {
100 Value::Number(n) => {
101 n.as_u64().ok_or(ClientError::InvalidUsageNumber {
102 usage_name: k.to_string(),
103 usage_number: n,
104 })
105 }
106 .map(|q| (k.to_string(), q)),
107 _ => Err(ClientError::InvalidUsageValue {
108 usage_name: k.to_string(),
109 usage_value: v,
110 }),
111 })
112 .collect::<Result<BTreeMap<String, u64>, ClientError>>()?,
113 ),
114 _ => None,
115 })
116 }
117}