1use reqwest::header::{HeaderMap, HeaderValue};
2use serde::de::DeserializeOwned;
3use serde_json::Value as JsonValue;
4
5use supabase_client_core::{StatusCode, SupabaseError, SupabaseResponse};
6
7use crate::sql::{CountOption, SqlOperation, SqlParts};
8
9pub async fn execute_rest<T: DeserializeOwned + Send>(
11 http: &reqwest::Client,
12 method: reqwest::Method,
13 url: &str,
14 mut headers: HeaderMap,
15 body: Option<JsonValue>,
16 api_key: &str,
17 schema: &str,
18 parts: &SqlParts,
19) -> SupabaseResponse<T> {
20 headers.insert("apikey", HeaderValue::from_str(api_key).unwrap());
22 headers.insert(
23 "Authorization",
24 HeaderValue::from_str(&format!("Bearer {}", api_key)).unwrap(),
25 );
26
27 if parts.schema_override.is_none() && schema != "public" {
29 match parts.operation {
30 SqlOperation::Select => {
31 headers
32 .entry("Accept-Profile")
33 .or_insert_with(|| HeaderValue::from_str(schema).unwrap());
34 }
35 _ => {
36 headers
37 .entry("Content-Profile")
38 .or_insert_with(|| HeaderValue::from_str(schema).unwrap());
39 }
40 }
41 }
42
43 headers
45 .entry("Accept")
46 .or_insert(HeaderValue::from_static("application/json"));
47
48 tracing::debug!(
49 method = %method,
50 url = %url,
51 "Executing PostgREST request"
52 );
53
54 let mut request = http.request(method.clone(), url).headers(headers);
55
56 if let Some(body) = body {
57 request = request.json(&body);
58 }
59
60 let response = match request.send().await {
61 Ok(r) => r,
62 Err(e) => return SupabaseResponse::error(SupabaseError::Http(e.to_string())),
63 };
64
65 let status_code = response.status().as_u16();
66 let resp_headers = response.headers().clone();
67
68 if method == reqwest::Method::HEAD || parts.head {
70 let count = parse_count_from_headers(&resp_headers);
71 if status_code >= 200 && status_code < 300 {
72 let mut resp = SupabaseResponse::<T>::ok(Vec::new());
73 if let Some(c) = count {
74 resp.count = Some(c);
75 }
76 return resp;
77 } else {
78 return SupabaseResponse::error(SupabaseError::postgrest(
79 status_code,
80 format!("HEAD request failed with status {}", status_code),
81 None,
82 ));
83 }
84 }
85
86 let body_text = match response.text().await {
88 Ok(t) => t,
89 Err(e) => return SupabaseResponse::error(SupabaseError::Http(e.to_string())),
90 };
91
92 if status_code >= 400 {
94 return parse_error_response(status_code, &body_text);
95 }
96
97 if status_code == 204 || body_text.is_empty() {
99 let count = parse_count_from_headers(&resp_headers);
100 let mut resp = SupabaseResponse::<T>::no_content();
101 resp.count = count;
102 return resp;
103 }
104
105 let count = parse_count_from_headers(&resp_headers);
107
108 if parts.single {
110 match serde_json::from_str::<T>(&body_text) {
112 Ok(item) => {
113 let mut resp = build_response_from_operation(vec![item], parts);
114 if let Some(c) = count {
115 resp.count = Some(c);
116 }
117 resp
118 }
119 Err(e) => SupabaseResponse::error(SupabaseError::Serialization(format!(
120 "Failed to parse single response: {}",
121 e
122 ))),
123 }
124 } else {
125 match serde_json::from_str::<Vec<T>>(&body_text) {
127 Ok(data) => {
128 let mut resp = build_response_from_operation(data, parts);
129 if let Some(c) = count {
130 resp.count = Some(c);
131 }
132 if parts.maybe_single {
134 match resp.data.len() {
135 0 | 1 => {}
136 n => return SupabaseResponse::error(SupabaseError::MultipleRows(n)),
137 }
138 }
139 resp
140 }
141 Err(_) => {
142 match serde_json::from_str::<T>(&body_text) {
144 Ok(item) => {
145 let mut resp = build_response_from_operation(vec![item], parts);
146 if let Some(c) = count {
147 resp.count = Some(c);
148 }
149 resp
150 }
151 Err(_) => {
152 match serde_json::from_str::<JsonValue>(&body_text) {
156 Ok(scalar) if !scalar.is_array() && !scalar.is_object() => {
157 let wrapped = format!(
158 "[{{\"{}\": {}}}]",
159 parts.table, body_text
160 );
161 match serde_json::from_str::<Vec<T>>(&wrapped) {
162 Ok(data) => {
163 let mut resp =
164 build_response_from_operation(data, parts);
165 if let Some(c) = count {
166 resp.count = Some(c);
167 }
168 resp
169 }
170 Err(e) => SupabaseResponse::error(
171 SupabaseError::Serialization(format!(
172 "Failed to parse scalar response: {}",
173 e
174 )),
175 ),
176 }
177 }
178 _ => SupabaseResponse::error(SupabaseError::Serialization(
179 format!(
180 "Failed to parse response: {}",
181 body_text
182 ),
183 )),
184 }
185 }
186 }
187 }
188 }
189 }
190}
191
192fn build_response_from_operation<T>(data: Vec<T>, parts: &SqlParts) -> SupabaseResponse<T> {
193 let status = match parts.operation {
194 SqlOperation::Insert | SqlOperation::Upsert => StatusCode::Created,
195 _ => StatusCode::Ok,
196 };
197
198 let count = if parts.count != CountOption::None {
199 Some(data.len() as i64)
200 } else {
201 None
202 };
203
204 SupabaseResponse {
205 data,
206 error: None,
207 count,
208 status,
209 }
210}
211
212fn parse_count_from_headers(headers: &HeaderMap) -> Option<i64> {
213 headers
215 .get("content-range")
216 .and_then(|v| v.to_str().ok())
217 .and_then(|s| {
218 if let Some(slash_pos) = s.rfind('/') {
219 let count_str = &s[slash_pos + 1..];
220 if count_str == "*" {
221 None
222 } else {
223 count_str.parse::<i64>().ok()
224 }
225 } else {
226 None
227 }
228 })
229}
230
231fn parse_error_response<T>(status_code: u16, body: &str) -> SupabaseResponse<T> {
232 if let Ok(error_obj) = serde_json::from_str::<JsonValue>(body) {
234 let message = error_obj
235 .get("message")
236 .and_then(|v| v.as_str())
237 .unwrap_or("Unknown error")
238 .to_string();
239 let code = error_obj
240 .get("code")
241 .and_then(|v| v.as_str())
242 .map(|s| s.to_string());
243
244 SupabaseResponse::error(SupabaseError::postgrest(status_code, message, code))
245 } else {
246 SupabaseResponse::error(SupabaseError::postgrest(
247 status_code,
248 body.to_string(),
249 None,
250 ))
251 }
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257 use reqwest::header::{HeaderMap, HeaderValue};
258 use serde_json::json;
259 use wiremock::matchers::{method, path};
260 use wiremock::{Mock, MockServer, ResponseTemplate};
261
262 #[test]
265 fn test_parse_count_range_format() {
266 let mut headers = HeaderMap::new();
267 headers.insert("content-range", HeaderValue::from_static("0-9/100"));
268 assert_eq!(parse_count_from_headers(&headers), Some(100));
269 }
270
271 #[test]
272 fn test_parse_count_star_range_format() {
273 let mut headers = HeaderMap::new();
274 headers.insert("content-range", HeaderValue::from_static("*/42"));
275 assert_eq!(parse_count_from_headers(&headers), Some(42));
276 }
277
278 #[test]
279 fn test_parse_count_star_count() {
280 let mut headers = HeaderMap::new();
281 headers.insert("content-range", HeaderValue::from_static("0-9/*"));
282 assert_eq!(parse_count_from_headers(&headers), None);
283 }
284
285 #[test]
286 fn test_parse_count_no_header() {
287 let headers = HeaderMap::new();
288 assert_eq!(parse_count_from_headers(&headers), None);
289 }
290
291 #[test]
292 fn test_parse_count_no_slash() {
293 let mut headers = HeaderMap::new();
294 headers.insert("content-range", HeaderValue::from_static("0-9"));
295 assert_eq!(parse_count_from_headers(&headers), None);
296 }
297
298 #[test]
299 fn test_parse_count_invalid_number() {
300 let mut headers = HeaderMap::new();
301 headers.insert("content-range", HeaderValue::from_static("0-9/abc"));
302 assert_eq!(parse_count_from_headers(&headers), None);
303 }
304
305 #[test]
306 fn test_parse_count_large_number() {
307 let mut headers = HeaderMap::new();
308 headers.insert("content-range", HeaderValue::from_static("0-99/1000000"));
309 assert_eq!(parse_count_from_headers(&headers), Some(1_000_000));
310 }
311
312 #[test]
315 fn test_parse_error_response_valid_json() {
316 let body = r#"{"message":"Row not found","code":"PGRST116","details":null,"hint":null}"#;
317 let resp: SupabaseResponse<JsonValue> = parse_error_response(404, body);
318 assert!(resp.is_err());
319 match resp.error.as_ref().unwrap() {
320 SupabaseError::PostgRest { status, message, code } => {
321 assert_eq!(*status, 404);
322 assert_eq!(message, "Row not found");
323 assert_eq!(code.as_deref(), Some("PGRST116"));
324 }
325 other => panic!("Expected PostgRest error, got {:?}", other),
326 }
327 }
328
329 #[test]
330 fn test_parse_error_response_valid_json_no_code() {
331 let body = r#"{"message":"Something went wrong"}"#;
332 let resp: SupabaseResponse<JsonValue> = parse_error_response(500, body);
333 assert!(resp.is_err());
334 match resp.error.as_ref().unwrap() {
335 SupabaseError::PostgRest { status, message, code } => {
336 assert_eq!(*status, 500);
337 assert_eq!(message, "Something went wrong");
338 assert!(code.is_none());
339 }
340 other => panic!("Expected PostgRest error, got {:?}", other),
341 }
342 }
343
344 #[test]
345 fn test_parse_error_response_no_message_field() {
346 let body = r#"{"error":"some error"}"#;
347 let resp: SupabaseResponse<JsonValue> = parse_error_response(400, body);
348 assert!(resp.is_err());
349 match resp.error.as_ref().unwrap() {
350 SupabaseError::PostgRest { message, .. } => {
351 assert_eq!(message, "Unknown error");
352 }
353 other => panic!("Expected PostgRest error, got {:?}", other),
354 }
355 }
356
357 #[test]
358 fn test_parse_error_response_invalid_json() {
359 let body = "this is not json";
360 let resp: SupabaseResponse<JsonValue> = parse_error_response(500, body);
361 assert!(resp.is_err());
362 match resp.error.as_ref().unwrap() {
363 SupabaseError::PostgRest { status, message, code } => {
364 assert_eq!(*status, 500);
365 assert_eq!(message, "this is not json");
366 assert!(code.is_none());
367 }
368 other => panic!("Expected PostgRest error, got {:?}", other),
369 }
370 }
371
372 fn make_select_parts(table: &str) -> SqlParts {
375 SqlParts::new(SqlOperation::Select, "public", table)
376 }
377
378 #[tokio::test]
379 async fn test_execute_rest_select_json_array() {
380 let mock_server = MockServer::start().await;
381 Mock::given(method("GET"))
382 .and(path("/rest/v1/users"))
383 .respond_with(
384 ResponseTemplate::new(200)
385 .set_body_json(json!([{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}])),
386 )
387 .mount(&mock_server)
388 .await;
389
390 let http = reqwest::Client::new();
391 let url = format!("{}/rest/v1/users", mock_server.uri());
392 let headers = HeaderMap::new();
393 let parts = make_select_parts("users");
394
395 let resp: SupabaseResponse<JsonValue> =
396 execute_rest(&http, reqwest::Method::GET, &url, headers, None, "test-key", "public", &parts).await;
397
398 assert!(resp.is_ok());
399 assert_eq!(resp.data.len(), 2);
400 assert_eq!(resp.data[0]["id"], 1);
401 assert_eq!(resp.data[0]["name"], "Alice");
402 assert_eq!(resp.data[1]["id"], 2);
403 assert_eq!(resp.data[1]["name"], "Bob");
404 }
405
406 #[tokio::test]
407 async fn test_execute_rest_select_single_object() {
408 let mock_server = MockServer::start().await;
409 Mock::given(method("GET"))
410 .and(path("/rest/v1/users"))
411 .respond_with(
412 ResponseTemplate::new(200)
413 .set_body_json(json!({"id": 1, "name": "Alice"})),
414 )
415 .mount(&mock_server)
416 .await;
417
418 let http = reqwest::Client::new();
419 let url = format!("{}/rest/v1/users", mock_server.uri());
420 let headers = HeaderMap::new();
421 let mut parts = make_select_parts("users");
422 parts.single = true;
423
424 let resp: SupabaseResponse<JsonValue> =
425 execute_rest(&http, reqwest::Method::GET, &url, headers, None, "test-key", "public", &parts).await;
426
427 assert!(resp.is_ok());
428 assert_eq!(resp.data.len(), 1);
429 assert_eq!(resp.data[0]["id"], 1);
430 assert_eq!(resp.data[0]["name"], "Alice");
431 }
432
433 #[tokio::test]
434 async fn test_execute_rest_head_with_count() {
435 let mock_server = MockServer::start().await;
436 Mock::given(method("HEAD"))
437 .and(path("/rest/v1/users"))
438 .respond_with(
439 ResponseTemplate::new(200)
440 .insert_header("content-range", "0-9/42"),
441 )
442 .mount(&mock_server)
443 .await;
444
445 let http = reqwest::Client::new();
446 let url = format!("{}/rest/v1/users", mock_server.uri());
447 let headers = HeaderMap::new();
448 let mut parts = make_select_parts("users");
449 parts.head = true;
450
451 let resp: SupabaseResponse<JsonValue> =
452 execute_rest(&http, reqwest::Method::HEAD, &url, headers, None, "test-key", "public", &parts).await;
453
454 assert!(resp.is_ok());
455 assert!(resp.data.is_empty());
456 assert_eq!(resp.count, Some(42));
457 }
458
459 #[tokio::test]
460 async fn test_execute_rest_head_error_status() {
461 let mock_server = MockServer::start().await;
462 Mock::given(method("HEAD"))
463 .and(path("/rest/v1/users"))
464 .respond_with(ResponseTemplate::new(401))
465 .mount(&mock_server)
466 .await;
467
468 let http = reqwest::Client::new();
469 let url = format!("{}/rest/v1/users", mock_server.uri());
470 let headers = HeaderMap::new();
471 let mut parts = make_select_parts("users");
472 parts.head = true;
473
474 let resp: SupabaseResponse<JsonValue> =
475 execute_rest(&http, reqwest::Method::HEAD, &url, headers, None, "test-key", "public", &parts).await;
476
477 assert!(resp.is_err());
478 match resp.error.as_ref().unwrap() {
479 SupabaseError::PostgRest { status, .. } => {
480 assert_eq!(*status, 401);
481 }
482 other => panic!("Expected PostgRest error, got {:?}", other),
483 }
484 }
485
486 #[tokio::test]
487 async fn test_execute_rest_4xx_error_json_body() {
488 let mock_server = MockServer::start().await;
489 Mock::given(method("GET"))
490 .and(path("/rest/v1/users"))
491 .respond_with(
492 ResponseTemplate::new(400)
493 .set_body_json(json!({"message": "Bad request", "code": "42703"})),
494 )
495 .mount(&mock_server)
496 .await;
497
498 let http = reqwest::Client::new();
499 let url = format!("{}/rest/v1/users", mock_server.uri());
500 let headers = HeaderMap::new();
501 let parts = make_select_parts("users");
502
503 let resp: SupabaseResponse<JsonValue> =
504 execute_rest(&http, reqwest::Method::GET, &url, headers, None, "test-key", "public", &parts).await;
505
506 assert!(resp.is_err());
507 match resp.error.as_ref().unwrap() {
508 SupabaseError::PostgRest { status, message, code } => {
509 assert_eq!(*status, 400);
510 assert_eq!(message, "Bad request");
511 assert_eq!(code.as_deref(), Some("42703"));
512 }
513 other => panic!("Expected PostgRest error, got {:?}", other),
514 }
515 }
516
517 #[tokio::test]
518 async fn test_execute_rest_204_no_content() {
519 let mock_server = MockServer::start().await;
520 Mock::given(method("DELETE"))
521 .and(path("/rest/v1/users"))
522 .respond_with(ResponseTemplate::new(204))
523 .mount(&mock_server)
524 .await;
525
526 let http = reqwest::Client::new();
527 let url = format!("{}/rest/v1/users", mock_server.uri());
528 let headers = HeaderMap::new();
529 let mut parts = SqlParts::new(SqlOperation::Delete, "public", "users");
530 parts.returning = None;
532
533 let resp: SupabaseResponse<JsonValue> =
534 execute_rest(&http, reqwest::Method::DELETE, &url, headers, None, "test-key", "public", &parts).await;
535
536 assert!(resp.is_ok());
537 assert!(resp.data.is_empty());
538 }
539
540 #[tokio::test]
541 async fn test_execute_rest_empty_body_response() {
542 let mock_server = MockServer::start().await;
543 Mock::given(method("POST"))
544 .and(path("/rest/v1/users"))
545 .respond_with(ResponseTemplate::new(200).set_body_string(""))
546 .mount(&mock_server)
547 .await;
548
549 let http = reqwest::Client::new();
550 let url = format!("{}/rest/v1/users", mock_server.uri());
551 let headers = HeaderMap::new();
552 let parts = SqlParts::new(SqlOperation::Insert, "public", "users");
553
554 let resp: SupabaseResponse<JsonValue> =
555 execute_rest(&http, reqwest::Method::POST, &url, headers, None, "test-key", "public", &parts).await;
556
557 assert!(resp.is_ok());
558 assert!(resp.data.is_empty());
559 }
560
561 #[tokio::test]
562 async fn test_execute_rest_insert_returns_created_status() {
563 let mock_server = MockServer::start().await;
564 Mock::given(method("POST"))
565 .and(path("/rest/v1/users"))
566 .respond_with(
567 ResponseTemplate::new(201)
568 .set_body_json(json!([{"id": 1, "name": "Alice"}])),
569 )
570 .mount(&mock_server)
571 .await;
572
573 let http = reqwest::Client::new();
574 let url = format!("{}/rest/v1/users", mock_server.uri());
575 let headers = HeaderMap::new();
576 let parts = SqlParts::new(SqlOperation::Insert, "public", "users");
577
578 let resp: SupabaseResponse<JsonValue> =
579 execute_rest(&http, reqwest::Method::POST, &url, headers, None, "test-key", "public", &parts).await;
580
581 assert!(resp.is_ok());
582 assert_eq!(resp.data.len(), 1);
583 assert_eq!(resp.status, supabase_client_core::StatusCode::Created);
584 }
585
586 #[tokio::test]
587 async fn test_execute_rest_with_count_header() {
588 let mock_server = MockServer::start().await;
589 Mock::given(method("GET"))
590 .and(path("/rest/v1/users"))
591 .respond_with(
592 ResponseTemplate::new(200)
593 .set_body_json(json!([{"id": 1}]))
594 .insert_header("content-range", "0-0/25"),
595 )
596 .mount(&mock_server)
597 .await;
598
599 let http = reqwest::Client::new();
600 let url = format!("{}/rest/v1/users", mock_server.uri());
601 let headers = HeaderMap::new();
602 let parts = make_select_parts("users");
603
604 let resp: SupabaseResponse<JsonValue> =
605 execute_rest(&http, reqwest::Method::GET, &url, headers, None, "test-key", "public", &parts).await;
606
607 assert!(resp.is_ok());
608 assert_eq!(resp.count, Some(25));
609 }
610
611 #[tokio::test]
612 async fn test_execute_rest_sets_auth_headers() {
613 let mock_server = MockServer::start().await;
614 Mock::given(method("GET"))
615 .and(path("/rest/v1/users"))
616 .and(wiremock::matchers::header("apikey", "my-secret-key"))
617 .and(wiremock::matchers::header("Authorization", "Bearer my-secret-key"))
618 .respond_with(ResponseTemplate::new(200).set_body_json(json!([])))
619 .mount(&mock_server)
620 .await;
621
622 let http = reqwest::Client::new();
623 let url = format!("{}/rest/v1/users", mock_server.uri());
624 let headers = HeaderMap::new();
625 let parts = make_select_parts("users");
626
627 let resp: SupabaseResponse<JsonValue> =
628 execute_rest(&http, reqwest::Method::GET, &url, headers, None, "my-secret-key", "public", &parts).await;
629
630 assert!(resp.is_ok());
631 }
632
633 #[tokio::test]
634 async fn test_execute_rest_non_public_schema_sets_profile() {
635 let mock_server = MockServer::start().await;
636 Mock::given(method("GET"))
637 .and(path("/rest/v1/users"))
638 .and(wiremock::matchers::header("Accept-Profile", "custom_schema"))
639 .respond_with(ResponseTemplate::new(200).set_body_json(json!([])))
640 .mount(&mock_server)
641 .await;
642
643 let http = reqwest::Client::new();
644 let url = format!("{}/rest/v1/users", mock_server.uri());
645 let headers = HeaderMap::new();
646 let parts = make_select_parts("users");
647
648 let resp: SupabaseResponse<JsonValue> =
649 execute_rest(&http, reqwest::Method::GET, &url, headers, None, "key", "custom_schema", &parts).await;
650
651 assert!(resp.is_ok());
652 }
653
654 #[tokio::test]
655 async fn test_execute_rest_with_body() {
656 let mock_server = MockServer::start().await;
657 Mock::given(method("POST"))
658 .and(path("/rest/v1/users"))
659 .and(wiremock::matchers::body_json(json!({"name": "Alice"})))
660 .respond_with(
661 ResponseTemplate::new(201)
662 .set_body_json(json!([{"id": 1, "name": "Alice"}])),
663 )
664 .mount(&mock_server)
665 .await;
666
667 let http = reqwest::Client::new();
668 let url = format!("{}/rest/v1/users", mock_server.uri());
669 let headers = HeaderMap::new();
670 let parts = SqlParts::new(SqlOperation::Insert, "public", "users");
671
672 let resp: SupabaseResponse<JsonValue> =
673 execute_rest(
674 &http, reqwest::Method::POST, &url, headers,
675 Some(json!({"name": "Alice"})), "key", "public", &parts,
676 ).await;
677
678 assert!(resp.is_ok());
679 assert_eq!(resp.data.len(), 1);
680 }
681
682 #[tokio::test]
683 async fn test_execute_rest_maybe_single_with_one_row() {
684 let mock_server = MockServer::start().await;
685 Mock::given(method("GET"))
686 .and(path("/rest/v1/users"))
687 .respond_with(
688 ResponseTemplate::new(200)
689 .set_body_json(json!([{"id": 1}])),
690 )
691 .mount(&mock_server)
692 .await;
693
694 let http = reqwest::Client::new();
695 let url = format!("{}/rest/v1/users", mock_server.uri());
696 let headers = HeaderMap::new();
697 let mut parts = make_select_parts("users");
698 parts.maybe_single = true;
699
700 let resp: SupabaseResponse<JsonValue> =
701 execute_rest(&http, reqwest::Method::GET, &url, headers, None, "key", "public", &parts).await;
702
703 assert!(resp.is_ok());
704 assert_eq!(resp.data.len(), 1);
705 }
706
707 #[tokio::test]
708 async fn test_execute_rest_maybe_single_with_multiple_rows_errors() {
709 let mock_server = MockServer::start().await;
710 Mock::given(method("GET"))
711 .and(path("/rest/v1/users"))
712 .respond_with(
713 ResponseTemplate::new(200)
714 .set_body_json(json!([{"id": 1}, {"id": 2}, {"id": 3}])),
715 )
716 .mount(&mock_server)
717 .await;
718
719 let http = reqwest::Client::new();
720 let url = format!("{}/rest/v1/users", mock_server.uri());
721 let headers = HeaderMap::new();
722 let mut parts = make_select_parts("users");
723 parts.maybe_single = true;
724
725 let resp: SupabaseResponse<JsonValue> =
726 execute_rest(&http, reqwest::Method::GET, &url, headers, None, "key", "public", &parts).await;
727
728 assert!(resp.is_err());
729 assert!(matches!(resp.error.as_ref().unwrap(), SupabaseError::MultipleRows(3)));
730 }
731
732 #[tokio::test]
733 async fn test_execute_rest_scalar_response() {
734 let mock_server = MockServer::start().await;
739 Mock::given(method("GET"))
740 .and(path("/rest/v1/my_func"))
741 .respond_with(
742 ResponseTemplate::new(200).set_body_string("42"),
743 )
744 .mount(&mock_server)
745 .await;
746
747 let http = reqwest::Client::new();
748 let url = format!("{}/rest/v1/my_func", mock_server.uri());
749 let headers = HeaderMap::new();
750 let parts = make_select_parts("my_func");
751
752 let resp: SupabaseResponse<JsonValue> =
753 execute_rest(&http, reqwest::Method::GET, &url, headers, None, "key", "public", &parts).await;
754
755 assert!(resp.is_ok());
756 assert_eq!(resp.data.len(), 1);
757 assert_eq!(resp.data[0], serde_json::json!(42));
759 }
760}