shopify_client/common/
http.rs1use crate::common::types::{APIError, RequestCallbacks};
2use reqwest::header::{HeaderMap, HeaderValue, CONTENT_TYPE};
3
4#[derive(serde::Serialize)]
5struct GraphQLRequest {
6 query: String,
7 variables: serde_json::Value,
8}
9
10#[derive(serde::Deserialize)]
11struct GraphQLResponse<T> {
12 data: Option<T>,
13 errors: Option<Vec<GraphQLError>>,
14}
15
16#[derive(serde::Deserialize, Debug)]
17struct GraphQLError {
18 message: String,
19}
20
21pub async fn execute_graphql<T: serde::de::DeserializeOwned>(
22 shop_url: &String,
23 version: &String,
24 access_token: &String,
25 callbacks: &RequestCallbacks,
26 query: String,
27 variables: serde_json::Value,
28) -> Result<T, APIError> {
29 let endpoint = format!("{}/admin/api/{}/graphql.json", shop_url, version);
30
31 let request_body = GraphQLRequest { query, variables };
32
33 let body_str = serde_json::to_string(&request_body).unwrap_or_default();
34
35 let mut callback_headers = HeaderMap::new();
36 callback_headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
37
38 callbacks.call_before(&endpoint, Some(&body_str), &callback_headers);
39
40 let client = reqwest::Client::new();
41 let response = client
42 .post(&endpoint)
43 .header("X-Shopify-Access-Token", access_token)
44 .header("Content-Type", "application/json")
45 .json(&request_body)
46 .send()
47 .await;
48
49 match response {
50 Ok(resp) => {
51 let response_headers = resp.headers().clone();
52 let response_text = resp.text().await.map_err(|_| APIError::FailedToParse)?;
53
54 callbacks.call_after(&endpoint, &response_text, &response_headers);
55
56 let graphql_response = serde_json::from_str::<GraphQLResponse<T>>(&response_text)
57 .map_err(|_| APIError::FailedToParse)?;
58
59 if let Some(errors) = graphql_response.errors {
60 let error_messages: Vec<String> =
61 errors.iter().map(|e| e.message.clone()).collect();
62 return Err(APIError::ServerError {
63 errors: error_messages.join(", "),
64 });
65 }
66
67 graphql_response.data.ok_or(APIError::FailedToParse)
68 }
69 Err(_) => Err(APIError::NetworkError),
70 }
71}