Skip to main content

shopify_client/admin/order/
remote.rs

1use reqwest::header::{HeaderMap, HeaderValue, CONTENT_TYPE};
2
3use crate::{
4    common::{
5        http::{execute_graphql, http_client},
6        types::APIError,
7        utils::parse_response_from_text,
8        ServiceContext,
9    },
10    types::order::{
11        GetOrderResp, OrderDetailByNameResp, OrderDetailResp, OrderDiscountsAndTransactionsResp,
12        OrderQueryResp, PatchOrderRequest,
13    },
14};
15
16pub async fn patch_order(
17    ctx: &ServiceContext,
18    order_id: &String,
19    patch_request: &PatchOrderRequest,
20) -> Result<GetOrderResp, APIError> {
21    let endpoint = format!(
22        "{}/admin/api/{}/orders/{}.json",
23        ctx.shop_url.trim_end_matches('/'),
24        ctx.version,
25        order_id
26    );
27
28    let body_str = serde_json::to_string(&patch_request).unwrap_or_default();
29
30    let mut callback_headers = HeaderMap::new();
31    callback_headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
32
33    ctx.callbacks
34        .call_before(&endpoint, Some(&body_str), &callback_headers);
35
36    let response = http_client()
37        .put(&endpoint)
38        .header("X-Shopify-Access-Token", &*ctx.access_token)
39        .header("Content-Type", "application/json")
40        .json(&patch_request)
41        .send()
42        .await;
43
44    match response {
45        Ok(resp) => {
46            let response_headers = resp.headers().clone();
47            let response_text = match resp.text().await {
48                Ok(text) => text,
49                Err(e) => {
50                    let error_msg = format!("<failed to read response body: {}>", e);
51                    ctx.callbacks
52                        .call_after(&endpoint, &error_msg, &response_headers);
53                    return Err(APIError::FailedToParse);
54                }
55            };
56
57            ctx.callbacks
58                .call_after(&endpoint, &response_text, &response_headers);
59
60            parse_response_from_text::<GetOrderResp>(&response_text)
61        }
62        Err(e) => {
63            let error_msg = format!("<network error: {}>", e);
64            ctx.callbacks
65                .call_after(&endpoint, &error_msg, &HeaderMap::new());
66            Err(APIError::NetworkError)
67        }
68    }
69}
70
71pub async fn get_order_with_name(
72    ctx: &ServiceContext,
73    order_name: &String,
74) -> Result<OrderQueryResp, APIError> {
75    let endpoint = format!(
76        "{}/admin/api/{}/orders.json?query=name:%23{}&status=any",
77        ctx.shop_url.trim_end_matches('/'),
78        ctx.version,
79        order_name
80    );
81
82    let callback_headers = HeaderMap::new();
83
84    ctx.callbacks
85        .call_before(&endpoint, None, &callback_headers);
86
87    let response = http_client()
88        .get(&endpoint)
89        .header("X-Shopify-Access-Token", &*ctx.access_token)
90        .send()
91        .await;
92
93    match response {
94        Ok(resp) => {
95            let response_headers = resp.headers().clone();
96            let response_text = match resp.text().await {
97                Ok(text) => text,
98                Err(e) => {
99                    let error_msg = format!("<failed to read response body: {}>", e);
100                    ctx.callbacks
101                        .call_after(&endpoint, &error_msg, &response_headers);
102                    return Err(APIError::FailedToParse);
103                }
104            };
105
106            ctx.callbacks
107                .call_after(&endpoint, &response_text, &response_headers);
108
109            parse_response_from_text::<OrderQueryResp>(&response_text)
110        }
111        Err(e) => {
112            let error_msg = format!("<network error: {}>", e);
113            ctx.callbacks
114                .call_after(&endpoint, &error_msg, &HeaderMap::new());
115            Err(APIError::NetworkError)
116        }
117    }
118}
119
120pub async fn get_order_with_id(
121    ctx: &ServiceContext,
122    order_id: &String,
123) -> Result<GetOrderResp, APIError> {
124    let endpoint = format!(
125        "{}/admin/api/{}/orders/{}.json",
126        ctx.shop_url.trim_end_matches('/'),
127        ctx.version,
128        order_id
129    );
130
131    let callback_headers = HeaderMap::new();
132
133    ctx.callbacks
134        .call_before(&endpoint, None, &callback_headers);
135
136    let response = http_client()
137        .get(&endpoint)
138        .header("X-Shopify-Access-Token", &*ctx.access_token)
139        .send()
140        .await;
141
142    match response {
143        Ok(resp) => {
144            let response_headers = resp.headers().clone();
145            let response_text = match resp.text().await {
146                Ok(text) => text,
147                Err(e) => {
148                    let error_msg = format!("<failed to read response body: {}>", e);
149                    ctx.callbacks
150                        .call_after(&endpoint, &error_msg, &response_headers);
151                    return Err(APIError::FailedToParse);
152                }
153            };
154
155            ctx.callbacks
156                .call_after(&endpoint, &response_text, &response_headers);
157
158            parse_response_from_text::<GetOrderResp>(&response_text)
159        }
160        Err(e) => {
161            let error_msg = format!("<network error: {}>", e);
162            ctx.callbacks
163                .call_after(&endpoint, &error_msg, &HeaderMap::new());
164            Err(APIError::NetworkError)
165        }
166    }
167}
168
169pub async fn get_order_discounts_and_transactions(
170    ctx: &ServiceContext,
171    order_gid: &str,
172    lines: u32,
173) -> Result<OrderDiscountsAndTransactionsResp, APIError> {
174    let query = r#"
175        query orderDiscountsAndTransactions($id: ID!, $lines: Int!) {
176            order(id: $id) {
177                discountCodes
178                transactions {
179                    gateway
180                    kind
181                    status
182                    amountSet { shopMoney { amount currencyCode } }
183                }
184                lineItems(first: $lines) {
185                    nodes {
186                        id
187                        discountAllocations {
188                            allocatedAmountSet { shopMoney { amount currencyCode } }
189                        }
190                    }
191                    pageInfo { hasNextPage }
192                }
193                cartDiscountAmountSet { shopMoney { amount currencyCode } }
194            }
195        }
196    "#;
197
198    let variables = serde_json::json!({ "id": order_gid, "lines": lines });
199
200    execute_graphql(ctx, query, variables).await
201}
202
203const ORDER_DETAIL_FIELDS: &str = r#"
204    id
205    name
206    email
207    phone
208    createdAt
209    currencyCode
210    displayFinancialStatus
211    taxesIncluded
212    discountCodes
213    cartDiscountAmountSet { shopMoney { amount currencyCode } }
214    customer {
215        id
216        firstName
217        lastName
218        defaultEmailAddress { emailAddress }
219        defaultPhoneNumber { phoneNumber }
220    }
221    shippingAddress {
222        name firstName lastName phone address1 address2
223        city province country countryCodeV2 zip
224    }
225    transactions {
226        gateway
227        kind
228        status
229        amountSet { shopMoney { amount currencyCode } }
230    }
231    fulfillments(first: $fulfillments) {
232        status
233        createdAt
234        updatedAt
235        trackingInfo { company number url }
236        fulfillmentLineItems(first: $lines) {
237            nodes { quantity lineItem { id } }
238        }
239    }
240    lineItems(first: $lines) {
241        nodes {
242            id
243            title
244            variantTitle
245            quantity
246            unfulfilledQuantity
247            sku
248            product { id }
249            variant { id }
250            originalUnitPriceSet { shopMoney { amount currencyCode } }
251            totalDiscountSet { shopMoney { amount currencyCode } }
252            discountAllocations { allocatedAmountSet { shopMoney { amount currencyCode } } }
253        }
254        pageInfo { hasNextPage }
255    }
256"#;
257
258pub async fn get_order_detail(
259    ctx: &ServiceContext,
260    order_gid: &str,
261    lines: u32,
262    fulfillments: u32,
263) -> Result<OrderDetailResp, APIError> {
264    let query = format!(
265        "query orderDetail($id: ID!, $lines: Int!, $fulfillments: Int!) {{ order(id: $id) {{ {ORDER_DETAIL_FIELDS} }} }}"
266    );
267
268    let variables = serde_json::json!({
269        "id": order_gid,
270        "lines": lines,
271        "fulfillments": fulfillments
272    });
273
274    execute_graphql(ctx, &query, variables).await
275}
276
277pub async fn find_order_detail_by_name(
278    ctx: &ServiceContext,
279    name: &str,
280    lines: u32,
281    fulfillments: u32,
282) -> Result<OrderDetailByNameResp, APIError> {
283    let query = format!(
284        "query orderDetailByName($search: String!, $lines: Int!, $fulfillments: Int!) {{ orders(first: 1, query: $search) {{ nodes {{ {ORDER_DETAIL_FIELDS} }} }} }}"
285    );
286
287    let variables = serde_json::json!({
288        "search": format!("name:{name}"),
289        "lines": lines,
290        "fulfillments": fulfillments
291    });
292
293    execute_graphql(ctx, &query, variables).await
294}