shopify_client/admin/order/
remote.rs1use 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}