Skip to main content

agent_sdk_store_supabase/
client.rs

1use std::sync::Arc;
2
3use agent_sdk_core::AgentError;
4use serde::Serialize;
5
6use crate::{
7    config::SupabaseStoreConfig,
8    transport::{SupabaseHttpRequest, SupabaseHttpResponse, SupabaseHttpTransport, supabase_error},
9};
10
11#[derive(Clone)]
12/// Supabase REST client for SDK store adapters.
13pub struct SupabaseClient {
14    config: SupabaseStoreConfig,
15    transport: Arc<dyn SupabaseHttpTransport>,
16}
17
18impl SupabaseClient {
19    /// Creates a Supabase client from config and transport.
20    pub fn new<T>(config: SupabaseStoreConfig, transport: T) -> Self
21    where
22        T: SupabaseHttpTransport + 'static,
23    {
24        Self {
25            config,
26            transport: Arc::new(transport),
27        }
28    }
29
30    /// Creates a Supabase client with a shared transport.
31    pub fn with_shared_transport(
32        config: SupabaseStoreConfig,
33        transport: Arc<dyn SupabaseHttpTransport>,
34    ) -> Self {
35        Self { config, transport }
36    }
37
38    /// Returns the client config.
39    pub fn config(&self) -> &SupabaseStoreConfig {
40        &self.config
41    }
42
43    /// Sends an RPC call through Supabase PostgREST.
44    pub fn rpc<T>(&self, function_name: &str, body: &T) -> Result<SupabaseHttpResponse, AgentError>
45    where
46        T: Serialize,
47    {
48        let request = self.request(
49            "POST",
50            &format!("/rest/v1/rpc/{function_name}"),
51            Some(serde_json::to_vec(body).map_err(|error| supabase_error(error.to_string()))?),
52        );
53        self.transport.send(request)
54    }
55
56    /// Sends a table insert request through Supabase PostgREST.
57    pub fn insert<T>(&self, table: &str, body: &T) -> Result<SupabaseHttpResponse, AgentError>
58    where
59        T: Serialize,
60    {
61        let request = self.request(
62            "POST",
63            &format!("/rest/v1/{table}"),
64            Some(serde_json::to_vec(body).map_err(|error| supabase_error(error.to_string()))?),
65        );
66        self.transport.send(request)
67    }
68
69    /// Sends a table select request through Supabase PostgREST.
70    pub fn select(&self, table: &str, query: &str) -> Result<SupabaseHttpResponse, AgentError> {
71        let request = self.request("GET", &format!("/rest/v1/{table}?{query}"), None);
72        self.transport.send(request)
73    }
74
75    fn request(&self, method: &str, path: &str, body: Option<Vec<u8>>) -> SupabaseHttpRequest {
76        SupabaseHttpRequest {
77            method: method.to_string(),
78            url: format!("{}{}", self.config.project_url(), path),
79            headers: vec![
80                (
81                    "apikey".to_string(),
82                    self.config.auth().api_key().to_string(),
83                ),
84                (
85                    "Authorization".to_string(),
86                    format!("Bearer {}", self.config.auth().bearer_token()),
87                ),
88                ("Accept".to_string(), "application/json".to_string()),
89                ("Content-Type".to_string(), "application/json".to_string()),
90                (
91                    "Accept-Profile".to_string(),
92                    self.config.schema().to_string(),
93                ),
94                (
95                    "Content-Profile".to_string(),
96                    self.config.schema().to_string(),
97                ),
98            ],
99            body,
100        }
101    }
102}