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