supabase_function_rs/
client.rs1use crate::errors::{FunctionsError};
2use crate::models::{FunctionInvokeOptions, FunctionRegion, FunctionsResponse, HttpMethod, InvokeBody, ResponseData};
3use reqwest::Client;
4use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
5use std::collections::HashMap;
6use std::convert::TryFrom;
7
8#[derive(Debug, Clone)]
9pub struct FunctionsClient {
10 url: String,
11 headers: HashMap<String, String>,
12 region: FunctionRegion,
13 client: Client,
14}
15
16impl FunctionsClient {
17 pub fn new(url: String, headers: Option<HashMap<String, String>>, region: Option<FunctionRegion>) -> Self {
18 Self {
19 url,
20 headers: headers.unwrap_or_default(),
21 region: region.unwrap_or(FunctionRegion::Any),
22 client: Client::new(),
23 }
24 }
25
26 pub fn set_auth(&mut self, token: String) {
27 self.headers.insert("Authorization".to_string(), format!("Bearer {}", token));
28 }
29
30 pub async fn invoke(
31 &self,
32 function_name: &str,
33 options: Option<FunctionInvokeOptions>,
34 ) -> Result<FunctionsResponse, FunctionsError> {
35 let options = options.unwrap_or_default();
36 let headers = self.headers.clone();
37
38 let mut req_headers = HeaderMap::new();
39 for (key, value) in headers {
40 req_headers.insert(
41 HeaderName::try_from(key.as_str()).map_err(|_| FunctionsError::FetchError("Invalid header name".into()))?,
42 HeaderValue::from_str(&value).map_err(|_| FunctionsError::FetchError("Invalid header value".into()))?,
43 );
44 }
45
46 if let Some(region) = options.region {
47 if region != FunctionRegion::Any {
48 req_headers.insert(
49 HeaderName::from_static("x-region"),
50 HeaderValue::from_str(region.to_string().as_str()).map_err(|_| FunctionsError::FetchError("Invalid region value".into()))?,
51 );
52 }
53 }
54
55 let method = options.method.unwrap_or(HttpMethod::Post);
56 let method_str = method.as_str();
57 let url = format!("{}/{}", self.url, function_name);
58
59
60 let request_builder = match options.body {
61 Some(InvokeBody::File(ref file)) |
62 Some(InvokeBody::Blob(ref file)) |
63 Some(InvokeBody::ArrayBuffer(ref file)) => {
64 req_headers.insert("Content-Type", HeaderValue::from_static("application/octet-stream"));
65 self.client.request(method_str.parse().unwrap(), &url).headers(req_headers).body(file.clone())
66 }
67 Some(InvokeBody::String(ref s)) => {
68 req_headers.insert("Content-Type", HeaderValue::from_static("text/plain"));
69 self.client.request(method_str.parse().unwrap(), &url).headers(req_headers).body(s.clone())
70 }
71 Some(InvokeBody::FormData(ref form_data)) => {
72 let form = reqwest::multipart::Form::new();
73 let form = form_data.iter().fold(form, |form, (key, value)| {
74 form.text(key.clone(), value.clone())
75 });
76 self.client.request(method_str.parse().unwrap(), &url).headers(req_headers).multipart(form)
77 }
78 Some(InvokeBody::Json(ref json)) => {
79 req_headers.insert("Content-Type", HeaderValue::from_static("application/json"));
80 self.client.request(method_str.parse().unwrap(), &url).headers(req_headers).json(json)
81 }
82 None => self.client.request(method_str.parse().unwrap(), &url).headers(req_headers),
83 };
84
85 let response = request_builder.send().await.map_err(|e| FunctionsError::FetchError(e.to_string()))?;
86
87
88 if let Some(is_relay_error) = response.headers().get("x-relay-error") {
89 if is_relay_error == "true" {
90 return Err(FunctionsError::RelayError("Relay Error invoking the Edge Function".into()));
91 }
92 }
93
94 if !response.status().is_success() {
95 return Err(FunctionsError::HttpError(response.status().to_string()));
96 }
97
98 let content_type = response
99 .headers()
100 .get(reqwest::header::CONTENT_TYPE)
101 .and_then(|v| v.to_str().ok())
102 .unwrap_or("text/plain")
103 .split(';')
104 .next()
105 .unwrap_or("text/plain");
106
107 let data = match content_type {
108 "application/json" => {
109 let json_data = response.json::<serde_json::Value>().await.map_err(|e| FunctionsError::FetchError(e.to_string()))?;
110 ResponseData::Json(json_data)
111 },
112 "application/octet-stream" => {
113 let bytes_data = response.bytes().await.map_err(|e| FunctionsError::FetchError(e.to_string()))?;
114 ResponseData::Bytes(bytes_data)
115 },
116 "text/event-stream" => {
117 let text_data = response.text().await.map_err(|e| FunctionsError::FetchError(e.to_string()))?;
118 ResponseData::Text(text_data)
119 },
120 "multipart/form-data" => {
121 let form_data = response.json::<HashMap<String, String>>().await.map_err(|e| FunctionsError::FetchError(e.to_string()))?;
122 ResponseData::FormData(form_data)
123 },
124 _ => {
125 let text_data = response.text().await.map_err(|e| FunctionsError::FetchError(e.to_string()))?;
126 ResponseData::Text(text_data)
127 }
128 };
129
130 Ok(FunctionsResponse::Success { data })
131 }
132}